<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Hyho.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 16 Jun 2024 16:10:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Hyho.log</title>
            <url>https://velog.velcdn.com/images/sot_sky/profile/1e02eec6-aeda-492f-b52d-71a7b53be2ef/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Hyho.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sot_sky" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[스택 활용 DFS]]></title>
            <link>https://velog.io/@sot_sky/%EC%8A%A4%ED%83%9D-%ED%99%9C%EC%9A%A9-DFS</link>
            <guid>https://velog.io/@sot_sky/%EC%8A%A4%ED%83%9D-%ED%99%9C%EC%9A%A9-DFS</guid>
            <pubDate>Sun, 16 Jun 2024 16:10:33 GMT</pubDate>
            <description><![CDATA[<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define MAX_VERTICES 50
#define TRUE 1
#define FALSE 0

typedef struct GraphNode{
    int vertex;
    struct GraphNode *link;
} GraphNode;

typedef struct GraphType{
    int n;
    GraphNode *adj_list[MAX_VERTICES];
} GraphType;

typedef struct Stack{
    int items[MAX_VERTICES];
    int top;
} Stack;

int visited[MAX_VERTICES];

void init_Graph(GraphType *g){
    g-&gt;n = 0;
    for(int i=0; i&lt;MAX_VERTICES; i++){
        g-&gt;adj_list[i] = NULL;
    }
}

void init_Stack(Stack *s){
    s-&gt;top = -1;
    for(int i=0; i&lt;MAX_VERTICES; i++){
        s-&gt;items[i] = 0;
    }
}

int is_Empty(Stack *s){
    return s-&gt;top == -1;
}

void push(Stack *s, int value){
    s-&gt;items[++s-&gt;top] = value;
}

int pop(Stack *s){
    if(is_Empty(s)){
        fprintf(stderr, &quot;stack underflow&quot;);
        return 0;
    }
    return s-&gt;items[s-&gt;top--];
}

void insert_vertex(GraphType *g, int vertex){
    if(((g-&gt;n)+1) &gt; MAX_VERTICES){
        fprintf(stderr, &quot;vertex overflow&quot;);
        return;
    }
    g-&gt;n++;
}

void insert_edge(GraphType *g, int u, int v){
    GraphNode *node;
    if(u&gt;=MAX_VERTICES || v&gt;=MAX_VERTICES){
        fprintf(stderr, &quot;edge error&quot;);
        return;
    }
    node = (GraphNode*)malloc(sizeof(GraphNode));
    node-&gt;vertex = v;
    node-&gt;link = g-&gt;adj_list[u];
    g-&gt;adj_list[u] = node;
}

void dfs(GraphType *g, int start){
    Stack *s;
    s = (Stack*)malloc(sizeof(Stack));
    init_Stack(s);
    push(s, start);
    while(!is_Empty(s)){
        start = pop(s);
        if(!visited[start]){
            visited[start] = TRUE;
            printf(&quot;%d -&gt; &quot;, start);

            GraphNode *node = g-&gt;adj_list[start];
            while(node){
                int adjvertex = node-&gt;vertex;
                if(!visited[adjvertex]){
                    push(s, adjvertex);
                }
                node = node-&gt;link;
            }
        }
    }
}

int main(){
    GraphType *g;
    g = (GraphType*)malloc(sizeof(GraphType));
    init_Graph(g);
    int vertex, edge, start;
    scanf(&quot;%d %d %d&quot;, &amp;vertex, &amp;edge, &amp;start);
    for(int i=0; i&lt;vertex; i++){
        insert_vertex(g, i);
    }
    int e1, e2;
    for(int i=0; i&lt;edge; i++){
        scanf(&quot;%d %d&quot;, &amp;e1, &amp;e2);
        insert_edge(g, e1, e2);
        insert_edge(g, e2, e1);
    }
    dfs(g, start);
    free(g);
    return 0;
}</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/055ef40b-9f3d-4a09-8dc6-ea76e5965829/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/b157361d-fdc3-4caf-906e-d0d0ad157913/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/2e4ad050-691a-4dd5-8ef3-59ce54c1a762/image.jpg" alt=""></p>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/70a94c89-cce6-4ce2-998c-3ec749c2515d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[큐 오름차순 BFS]]></title>
            <link>https://velog.io/@sot_sky/%ED%81%90-%EC%98%A4%EB%A6%84%EC%B0%A8%EC%88%9C-BFS</link>
            <guid>https://velog.io/@sot_sky/%ED%81%90-%EC%98%A4%EB%A6%84%EC%B0%A8%EC%88%9C-BFS</guid>
            <pubDate>Sat, 15 Jun 2024 15:57:38 GMT</pubDate>
            <description><![CDATA[<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define MAX_VERTICES 100001
#define TRUE 1
#define FALSE 0

typedef struct GraphNode{
    int vertex;
    struct GraphNode *link;
} GraphNode;

typedef struct GraphType{
    int n;
    GraphNode *adj_list[MAX_VERTICES];
} GraphType;

typedef struct Queue{
    int items[MAX_VERTICES];
    int front, rear;
} Queue;

void init_Queue(Queue *q){
    q-&gt;front = q-&gt;rear = 0;
}

void enqueue(Queue *q, int value){
    if(q-&gt;rear &gt; MAX_VERTICES){
        fprintf(stderr, &quot;queue overflow&quot;);
        return;
    }
    q-&gt;items[q-&gt;rear++] = value;
}

int is_Empty(Queue *q){
    return q-&gt;front == q-&gt;rear;
}

int dequeue(Queue *q){
    if(is_Empty(q)){
        fprintf(stderr, &quot;queue empty&quot;);
        return 0;
    }
    return q-&gt;items[q-&gt;front++];
}

int visited[MAX_VERTICES];
int i = 1;

void init(GraphType *g){
    g-&gt;n = 0;
    for(int v=0; v&lt;MAX_VERTICES; v++){
        g-&gt;adj_list[v] = NULL;
    }
}

void insert_vertex(GraphType *g, int v){
    if(((g-&gt;n)+1)&gt;MAX_VERTICES){
        fprintf(stderr, &quot;vertex overflow&quot;);
        return;
    }
    g-&gt;n++;
}

void insert_edge(GraphType *g, int u, int v){
    GraphNode *node;
    if(u&gt;=MAX_VERTICES || v&gt;=MAX_VERTICES){
        fprintf(stderr, &quot;edge error&quot;);
        return;
    }
    node = (GraphNode*)malloc(sizeof(GraphNode));
    node-&gt;vertex = v;
    node-&gt;link = g-&gt;adj_list[u];
    g-&gt;adj_list[u] = node;
}

void split_list(GraphNode *node, GraphNode **front, GraphNode **back){
    GraphNode *fast, *slow;
    slow = node;
    fast = node-&gt;link;

    while(fast != NULL){
        fast = fast-&gt;link;
        if(fast != NULL){
            slow = slow-&gt;link;
            fast = fast-&gt;link;
        }
    }

    *front = node;
    *back = slow-&gt;link;
    slow-&gt;link = NULL;
}

GraphNode *sorted_merge(GraphNode *front, GraphNode *back){
    GraphNode *res;

    if(front == NULL){
        return back;
    }
    if(back == NULL){
        return front;
    }

    if(front-&gt;vertex &gt; back-&gt;vertex){
        res = back;
        res-&gt;link = sorted_merge(front, back-&gt;link);
    } else{
        res = front;
        res-&gt;link = sorted_merge(front-&gt;link, back);
    }
    return res;
}

GraphNode *merge_sort(GraphNode *node){
    if(node == NULL || node-&gt;link == NULL){
        return node;
    }
    GraphNode *a;
    GraphNode *b;
    split_list(node, &amp;a, &amp;b);

    a = merge_sort(a);
    b = merge_sort(b);

    return sorted_merge(a,b);
}

void bfs(GraphType *g, int start){
    GraphNode *node;
    Queue *q;
    q = (Queue*)malloc(sizeof(Queue));
    init_Queue(q);
    visited[start] = i++;
    enqueue(q, start);
    while(!is_Empty(q)){
        start = dequeue(q);
        for(node = merge_sort(g-&gt;adj_list[start]); node!=NULL; node = node-&gt;link){
            if(!visited[node-&gt;vertex]){
                visited[node-&gt;vertex] = i++;
                enqueue(q, node-&gt;vertex);
            }
        }
    }
}

void print_list(GraphType *g){
    GraphNode *node;
    int r,c;
    for(r=1; r&lt;=g-&gt;n; r++){
        printf(&quot;%d : &quot;, r);
        for(node = g-&gt;adj_list[r]; node != NULL; node = node-&gt;link){
            printf(&quot;%d -&gt; &quot;, node-&gt;vertex);
        }
        printf(&quot;\n&quot;);
    }
}

int main(){
    GraphType *g;
    g = (GraphType*)malloc(sizeof(GraphType));
    init(g);
    int vertex, edge, start;
    scanf(&quot;%d %d %d&quot;, &amp;vertex, &amp;edge, &amp;start);
    for(int i=0; i&lt;vertex; i++){
        insert_vertex(g, i);
    }
    int e1, e2;
    for(int i=0; i&lt;edge; i++){
        scanf(&quot;%d %d&quot;, &amp;e1, &amp;e2);
        insert_edge(g, e1, e2);
        insert_edge(g, e2, e1);
    }
    bfs(g, start);
    for(int i=1; i&lt;=g-&gt;n; i++){
        if(visited[i] == 0){
            printf(&quot;0\n&quot;);
        } else{
            printf(&quot;%d\n&quot;, visited[i]);
        }
    }
    free(g);
    return 0;
}</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/019902c6-dd51-4690-a920-97dae7650219/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DFS ]]></title>
            <link>https://velog.io/@sot_sky/%EC%8A%A4%ED%83%9D%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-DFS</link>
            <guid>https://velog.io/@sot_sky/%EC%8A%A4%ED%83%9D%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-DFS</guid>
            <pubDate>Sun, 09 Jun 2024 16:27:01 GMT</pubDate>
            <description><![CDATA[<h2 id="인접-행렬">인접 행렬</h2>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define MAX_VERTICES 50
#define TRUE 1
#define FALSE 0

typedef struct GraphType{
    int n;
    int adj_mat[MAX_VERTICES][MAX_VERTICES];
} GraphType;

typedef struct Stack{
    int items[MAX_VERTICES];
    int top;
} Stack;

int visited[MAX_VERTICES];

void init(GraphType *g, Stack *s){
    g-&gt;n = 0;
    s-&gt;top = -1;
    int r, c;
    for(r = 0; r&lt;MAX_VERTICES; r++){
        s-&gt;items[r] = 0;
        for(c = 0; c&lt;MAX_VERTICES; c++){
            g-&gt;adj_mat[r][c] = 0;
        }
    }

}

int is_Empty(Stack *s){
    return s-&gt;top == -1;
}

void push(Stack *s, int value){
    s-&gt;items[++s-&gt;top] = value;
}

int pop(Stack *s){
    if(is_Empty(s)){
        fprintf(stderr, &quot;underflow&quot;);
        return 0;
    }
    return s-&gt;items[s-&gt;top--];
}

void insert_vertex(GraphType *g, int v){
    if(((g-&gt;n)+1) &gt; MAX_VERTICES){
        fprintf(stderr, &quot;vertex overflow&quot;);
        return;
    }
    g-&gt;n++;
}

void insert_edge(GraphType *g, int start, int end){
    if(start &gt;= g-&gt;n || end &gt;= g-&gt;n){
        fprintf(stderr, &quot;edge error&quot;);
        return;
    }
    g-&gt;adj_mat[start][end] = 1;
    g-&gt;adj_mat[end][start] = 1;
}

void dfs(GraphType *g, int start){
    visited[start] = TRUE;
    printf(&quot;%d -&gt;&quot; ,start);
    for(int i=1; i&lt;=g-&gt;n; i++){
        if(g-&gt;adj_mat[start][i] &amp;&amp; !visited[i]){
            dfs(g, i);
        }
    }
}

void init_Visited(){
    for(int i=0; i&lt;MAX_VERTICES; i++){
        visited[i] = FALSE;
    }
}

int main(){
    GraphType *g;
    Stack stack;
    g = (GraphType*)malloc(sizeof(GraphType));
    init(g, &amp;stack);
    int vertex, edge, start;
    int e1, e2;
    scanf(&quot;%d %d&quot;, &amp;vertex, &amp;edge);
    for(int i=1; i&lt;=vertex; i++){
        insert_vertex(g, i);
    }
    for(int i=0; i&lt;edge; i++){
        scanf(&quot;%d %d&quot;, &amp;e1, &amp;e2);
        insert_edge(g, e1, e2);
        insert_edge(g, e2, e1);
    }
    int count;
    printf(&quot;vertex input : &quot;);
    scanf(&quot;%d&quot;, &amp;count);
    for(int i=0; i&lt;count; i++){
        scanf(&quot;%d&quot;, &amp;start);
        push(&amp;stack, start);
    }
    while(!is_Empty(&amp;stack)){
        int v = pop(&amp;stack);
        init_Visited();
        printf(&quot;vertex %d dfs\n&quot;, v);
        dfs(g, v);
        printf(&quot;\n&quot;);
    }

}</code></pre><h2 id="인접-리스트">인접 리스트</h2>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define MAX_VERTICES 50
#define TRUE 1
#define FALSE 0

typedef struct Stack{
    int items[MAX_VERTICES];
    int top;
} Stack;

typedef struct GraphNode{
    int vertex;
    struct GraphNode *link;
} GraphNode;

typedef struct GraphType{
    int n;
    GraphNode *adj_list[MAX_VERTICES];
} GraphType;

int visited[MAX_VERTICES];

void init(GraphType *g, Stack *s){
    g-&gt;n = 0;
    s-&gt;top = -1;
    for(int i=0; i&lt;MAX_VERTICES; i++){
        g-&gt;adj_list[i] = NULL;
        s-&gt;items[i] = 0;
    }
}

int is_Empty(Stack *s){
    return s-&gt;top == -1;
}

void push(Stack *s, int vertex){
    s-&gt;items[++s-&gt;top] = vertex;
}

int pop(Stack *s){
    if(is_Empty(s)){
        fprintf(stderr, &quot;stack underflow&quot;);
        return 0;
    }
    return s-&gt;items[s-&gt;top--];
}

void insert_vertex(GraphType *g, int v){
    if(((g-&gt;n)+1) &gt; MAX_VERTICES){
        fprintf(stderr, &quot;vertex overflow&quot;);
        return;
    }
    g-&gt;n++;
}

void insert_edge(GraphType *g, int u, int v){
    GraphNode *node;
    if(u&gt;=g-&gt;n || v&gt;=g-&gt;n){
        fprintf(stderr, &quot;edge error&quot;);
        return;
    }
    node = (GraphNode*)malloc(sizeof(GraphNode));
    node-&gt;vertex = v;
    node-&gt;link = g-&gt;adj_list[u];
    g-&gt;adj_list[u] = node;
}

void dfs(GraphType *g, int start){
    GraphNode *node;
    visited[start] = TRUE;
    printf(&quot;%d -&gt; &quot;, start);
    node = g-&gt;adj_list[start];
    for(node; node != NULL; node = node-&gt;link){
        if(!visited[node-&gt;vertex]){
            dfs(g, node-&gt;vertex);
        }
    }
}

void init_Visited(){
    for(int i=0; i&lt;MAX_VERTICES; i++){
        visited[i] = 0;
    }
}

int main(){
    GraphType *g;
    Stack stack;
    g = (GraphType*)malloc(sizeof(GraphType));
    init(g, &amp;stack);
    int vertex, edge, num, start;
    scanf(&quot;%d %d&quot;, &amp;vertex, &amp;edge);
    int e1, e2;

    for(int i=1; i&lt;=vertex; i++){
        insert_vertex(g, i);
    }
    for(int i=0; i&lt;edge; i++){
        scanf(&quot;%d %d&quot;, &amp;e1, &amp;e2);
        insert_edge(g, e1, e2);
        insert_edge(g, e2, e1);
    }

    printf(&quot;vertex count : &quot;);
    scanf(&quot;%d &quot;, &amp;num);
    for(int i=0; i&lt;num; i++){
        scanf(&quot;%d&quot;, &amp;start);
        push(&amp;stack, start);
    }

    while(!is_Empty(&amp;stack)){
        init_Visited();
        int v = pop(&amp;stack);
        printf(&quot;vertex : %d\n&quot;, v);
        dfs(g, v);
        printf(&quot;\n&quot;);
    }
    free(g);
    return 0;


}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[연결 리스트 정렬]]></title>
            <link>https://velog.io/@sot_sky/%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@sot_sky/%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Thu, 06 Jun 2024 15:55:29 GMT</pubDate>
            <description><![CDATA[<h2 id="합병-정렬">합병 정렬</h2>
<pre><code>void split_list(GraphNode *node, GraphNode **front_ref, GraphNode **back_ref){
    GraphNode *fast;
    GraphNode *slow;
    slow = node;
    fast = node-&gt;link;

    while(fast != NULL){
        fast = fast-&gt;link;
        if(fast != NULL){
            slow = slow-&gt;link;
            fast = fast-&gt;link;
        }
    }

    *front_ref = node;
    *back_ref = slow-&gt;link;
    slow-&gt;link = NULL;
}

GraphNode* sorted_merge(GraphNode *a, GraphNode *b){
    GraphNode *result = NULL;

    if(a == NULL)
        return b;
    else if(b == NULL)
        return a;

    if(a-&gt;vertex &gt;= b-&gt;vertex){
        result = a;
        result-&gt;link = sorted_merge(a-&gt;link, b);
    } else{
        result = b;
        result-&gt;link = sorted_merge(a, b-&gt;link);
    }
    return result;
}

GraphNode* merge_sort(GraphNode *node){
    if(node == NULL || node-&gt;link == NULL){
        return node;
    }

    GraphNode *a;
    GraphNode *b;

    split_list(node, &amp;a, &amp;b);

    a = merge_sort(a);
    b = merge_sort(b);

    return sorted_merge(a, b);
}</code></pre><p>합병 정렬의 경우 시간복잡도는 O(n log n)으로 큰 문제를 작은 문제로 분할하여 해결하기 때문에 일반적인 정렬에 비해 시간 복잡도가 낮으며 특히 많은 데이터를 정렬할 때 사용하면 효율적이다.</p>
<p>내림차순의 합병 정렬 과정은 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/f288629a-5649-4c90-b2d3-0e53991f0d81/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DFS(깊이 우선 탐색)]]></title>
            <link>https://velog.io/@sot_sky/DFS%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89</link>
            <guid>https://velog.io/@sot_sky/DFS%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89</guid>
            <pubDate>Tue, 04 Jun 2024 07:50:26 GMT</pubDate>
            <description><![CDATA[<h2 id="1인접-행렬에서의-dfs">1.인접 행렬에서의 DFS</h2>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define TRUE 1
#define FALSE 0
#define MAX_VERTICES 50

typedef struct GraphType{
    int n;
    int adj_mat[MAX_VERTICES][MAX_VERTICES];
} GraphType;

int visited[MAX_VERTICES]; // 방문 정점

void init(GraphType *g){
    int r,c;
    g-&gt;n=0;
    for(r=0; r&lt;MAX_VERTICES; r++){
        for(c=0; c&lt;MAX_VERTICES; c++){
            g-&gt;adj_mat[r][c] = 0;
        }
    }
}

void insert_vertex(GraphType *g, int v){
    if(((g-&gt;n)+1 &gt; MAX_VERTICES)){
        fprintf(stderr,&quot;vertex overflow&quot;);
        return;
    }
    g-&gt;n++;
}

void insert_edge(GraphType *g, int start, int end){
    if(start &gt;= g-&gt;n || end &gt;= g-&gt;n){
        fprintf(stderr, &quot;edge error&quot;);
        return;
    }
    g-&gt;adj_mat[start][end] = 1;
    g-&gt;adj_mat[end][start] = 1;
}

// void print_adj_mat(GraphType *g){
//     for(int i=0; i&lt;g-&gt;n; i++){
//         for(int j=0; j&lt;g-&gt;n; j++){
//             printf(&quot;%2d&quot;, g-&gt;adj_mat[i][j]);
//         }
//         printf(&quot;\n&quot;);
//     }
// }

void dfs_mat(GraphType *g, int v){
    int w;
    visited[v] = TRUE;
    printf(&quot;vertex %d -&gt; &quot;, v);
    for(w=0; w&lt;g-&gt;n; w++){
        if(g-&gt;adj_mat[v][w] &amp;&amp; !visited[w]){
            dfs_mat(g, w);
        }
    }
}

int main(){
    GraphType *g;
    g = (GraphType*)malloc(sizeof(GraphType));
    init(g);
    for(int i=0; i&lt;4; i++){
        insert_vertex(g, i);
    }
    insert_edge(g, 0, 1);
    insert_edge(g, 0, 2);
    insert_edge(g, 0, 3);
    insert_edge(g, 1, 2);
    insert_edge(g, 2, 3);

    printf(&quot;dfs\n&quot;);
    dfs_mat(g,0);
    printf(&quot;\n&quot;);
    free(g);
    return 0;
}</code></pre><p>현재 그래프의 상태는 다음과 같다</p>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/940fe788-dade-4720-bf1a-3f23f5b22ddc/image.png" alt="">
인접행렬(adj_mat[][])로 나타낼 경우 다음과 같다.
--0 1 2 3
0 0 1 1 1 
1 1 0 1 1
2 1 1 0 1
3 1 0 1 0</p>
<p>DFS 탐색 함수 부분을 보자</p>
<pre><code>void dfs_mat(GraphType *g, int v){
    int w;
    visited[v] = TRUE;
    printf(&quot;vertex %d -&gt; &quot;, v);
    for(w=0; w&lt;g-&gt;n; w++){
        if(g-&gt;adj_mat[v][w] &amp;&amp; !visited[w]){
            dfs_mat(g, w);
        }
    }
}</code></pre><p>dfs_mat(g, 0) 호출 시 일어나는 과정을 한 단계씩 분석해보자</p>
<p>dfs_mat(g, 0) 즉, 0의 위치에서 dfs 탐색을 시도하였다.</p>
<ol>
<li>visited[0] = TRUE</li>
<li>printf(&quot;vertex 0 -&gt;&quot;)</li>
<li>g-&gt;adj_mat[0][0] &amp;&amp; !visited[0] 의 결과는 0 &amp;&amp; 1 이므로 거짓이 된다</li>
<li>g-&gt;adj_mat[0][1] &amp;&amp; !visited[1] 의 결과는 1 &amp;&amp; 1 이므로 참이 되어 재귀 호출을 하게 된다.</li>
</ol>
<p>dfs_mat(g, 1)
5. visited[1] = TRUE
6. printf(&quot;vertex 1 -&gt;&quot;)
7. g-&gt;adj_mat[1][0] &amp;&amp; !visited[0] 의 결과는 1 &amp;&amp; 0 이므로 거짓이 된다
8. g-&gt;adj_mat[1][1] &amp;&amp; !visited[1] 의 결과는 0 &amp;&amp; 0 이므로 거짓이 된다
9. g-&gt;Adj_mat[1][2] &amp;&amp; !visited[2] 의 결과는 1 &amp;&amp; 1 이므로 참이 되어 재귀 호출을 하게 된다.</p>
<p>dfs_mat(g, 2)
10. visited[2] = TRUE
11. printf(&quot;vertex 2 -&gt;&quot;)
12. g-&gt;adj_mat[2][0] &amp;&amp; !visited[0] 의 결과는 1 &amp;&amp; 0 이므로 거짓이 된다
13. g-&gt;adj_mat[2][1] &amp;&amp; !visited[1] 의 결과는 1 &amp;&amp; 0 이므로 거짓이 된다
14. g-&gt;adj_mat[2][2] &amp;&amp; !visited[2] 의 결과는 0 &amp;&amp; 0 이므로 거짓이 된다
15. g-&gt;adj_mat[2][3] &amp;&amp; !visited[3] 의 결과는 1 &amp;&amp; 1 이므로 참이 되어 재귀 호출을 하게 된다.</p>
<p>dfs_mat(g, 3)
16. visited[3] = TRUE
17. printf(&quot;vertex 3 -&gt;&quot;)
18. g-&gt;adj_mat[3][0] &amp;&amp; !visited[0] 의 결과는 1 &amp;&amp; 0 이므로 거짓이 된다
19. g-&gt;adj_mat[3][1] &amp;&amp; !visited[1] 의 결과는 0 &amp;&amp; 0 이므로 거짓이 된다
20. g-&gt;adj_mat[3][2] &amp;&amp; !visited[2] 의 결과는 1 &amp;&amp; 0 이므로 거짓이 된다
21. g-&gt;adj_mat[3][3] &amp;&amp; !visited[3] 의 결과는 0 &amp;&amp; 0 이므로 거짓이 된다</p>
<p>&amp;nbsp
&amp;nbsp
결과 출력 값은 다음과 같다
<img src="https://velog.velcdn.com/images/sot_sky/post/498f6557-f43f-4744-9c18-f2ff248e1d7c/image.png" alt=""></p>
<h2 id="2인접-리스트에서의-dfs">2.인접 리스트에서의 DFS</h2>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define TRUE 1
#define FALSE 0
#define MAX_VERTICES 50

typedef struct GraphNode{
    int vertex;
    struct GraphNode *link;
} GraphNode;

typedef struct GraphType{
    int n;
    GraphNode *adj_list[MAX_VERTICES];
} GraphType;

int visited[MAX_VERTICES];

void init(GraphType *g){
    int v;
    g-&gt;n = 0;
    for(v=0; v&lt;MAX_VERTICES; v++){
        g-&gt;adj_list[v] = NULL;
    }
}

void insert_vertex(GraphType *g, int v){
    if(((g-&gt;n)+1) &gt; MAX_VERTICES){
        fprintf(stderr, &quot;vertex overflow&quot;);
        return;
    }
    g-&gt;n++;
}

void insert_edge(GraphType *g, int u, int v){
    GraphNode *node;
    if(u&gt;=g-&gt;n || v&gt;=g-&gt;n){
        fprintf(stderr, &quot;edge error&quot;);
        return;
    }
    node = (GraphNode*)malloc(sizeof(GraphNode));
    node-&gt;vertex = v;
    node-&gt;link = g-&gt;adj_list[u];
    g-&gt;adj_list[u] = node;
}

void dfs_list(GraphType *g, int v){
    GraphNode *node;
    visited[v] = TRUE;
    printf(&quot;vertex %d -&gt;&quot;, v);
    for(node = g-&gt;adj_list[v]; node; node = node-&gt;link){
        if(!visited[node-&gt;vertex]){
            dfs_list(g, node-&gt;vertex);
        }
    }
}

int main(){
    GraphType *g;
    g = (GraphType*)malloc(sizeof(GraphType));
    init(g);

    for(int i=0; i&lt;4; i++){
        insert_vertex(g, i);
    }

    insert_edge(g, 0, 1);
    insert_edge(g, 1, 0);
    insert_edge(g, 0, 2);
    insert_edge(g, 2, 0);
    insert_edge(g, 0, 3);
    insert_edge(g, 3, 0);
    insert_edge(g, 1, 2);
    insert_edge(g, 2, 1);
    insert_edge(g, 2, 3);
    insert_edge(g, 3, 2);

    printf(&quot;dfs\n&quot;);
    dfs_list(g, 0);
    free(g);
    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 - 4]]></title>
            <link>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-4</link>
            <guid>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-4</guid>
            <pubDate>Fri, 01 Sep 2023 01:40:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-자료형-변환">1. 자료형 변환</h1>
<h3 id="1-1-문자와-숫자의-연산">1-1) 문자와 숫자의 연산</h3>
<p>&quot;10&quot;이라는 문자열과 10이라는 숫자를 곱하면 어떻게 될까. 자바스크립트는 실행 시 내부적으로 자료형 변환을 하여 연산한다. 따라서 100이 출력된다.
<img src="https://velog.velcdn.com/images/sot_sky/post/bfe4cb67-6ba2-4246-be61-a7c67936b7b6/image.png" alt=""></p>
<h3 id="1-2-문자열-입력-함수-prompt">1-2) 문자열 입력 함수 prompt()</h3>
<p>prompt 함수에 대해 알아보자. 사용자에게 입력을 요구하는 입력창을 띄우는 함수로 2가지 매개변수를 받는다.</p>
<pre><code>&lt;script&gt;
    const input = prompt(&#39;메세지&#39;, &#39;디폴트&#39;)
    alert(input)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/8e8af4f7-ebc5-4515-a171-0c6bc3319ea3/image.png" alt="">
입력 창이 나타나고 입력 시 alert 창에 출력된다. 입력한 매개변수의 위치도 알 수 있다.</p>
<h3 id="1-3-불-입력-함수-confirm">1-3) 불 입력 함수 confirm()</h3>
<p>confirm 함수는 prompt와 비슷하게 동작한다. 사용자에게 확인을 요구하는 메시지 창이 나타나고 <code>확인</code>을 클릭하면 true가 반환되고 <code>취소</code>를 클릭하면 false가 반환된다.</p>
<pre><code>&lt;script&gt;
    const input = confirm(&#39;수락하시겠습니까?&#39;)
    alert(input)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/1b54f3ab-9065-46d2-9791-f2cb5b146544/image.png" alt="">
확인 클릭 시 true 출력
<img src="https://velog.velcdn.com/images/sot_sky/post/7d90736e-31b8-4131-9945-a12103322701/image.png" alt="">
취소 클릭 시 false 출력
<img src="https://velog.velcdn.com/images/sot_sky/post/abd27954-0916-4e86-ac82-eb975c3cc8d1/image.png" alt=""></p>
<h3 id="1-4-숫자-자료형-변환-함수-number">1-4) 숫자 자료형 변환 함수 Number()</h3>
<p>Number 함수는 숫자로 적힌 문자열을 숫자로 변환해준다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8c4f1a7d-6d32-4b27-8dc7-1bb80b35e93c/image.png" alt="">
자료형은 숫자이지만 숫자로 나타낼 수 없는 숫자는 Nan(Not a Number)이라는 값을 출력한다.
<img src="https://velog.velcdn.com/images/sot_sky/post/039d7fad-679f-4231-9df5-4e997df790d6/image.png" alt=""></p>
<h3 id="1-5-문자열-자료형-변환-함수-string">1-5) 문자열 자료형 변환 함수 String()</h3>
<p>다른 자료형을 문자열로 변환할 때 사용하는 함수이다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f10561b6-bf46-4d43-839b-0b3fd63d928f/image.png" alt="">
문자열로 변환 중 또다른 방법은 <code>+ &quot;&quot;</code>를 하는 방법도 있다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2a4434c7-f521-4d12-91ba-d01670f67666/image.png" alt=""></p>
<h3 id="1-6-불-자료형으로-변환-함수-boolean">1-6) 불 자료형으로 변환 함수 Boolean()</h3>
<p>거의 대부분의 자료는 불로 변환 시 true로 변환되지만 몇 가지는 false가 반환된다.</p>
<pre><code>&gt; Boolean(0)
false

&gt; Boolean(NaN)
false

&gt; Boolean(&quot;&quot;)
false

&gt; Boolean(&#39;&#39;)
false

&gt; Boolean(null)
false

&gt; Boolean(undefined)
false</code></pre><h1 id="2-예제">2. 예제</h1>
<h3 id="2-1-inch-단위를-cm-단위로-변경하기">2-1) inch 단위를 cm 단위로 변경하기</h3>
<p>일단 inch에 * 2.54를 해주어야 cm 단위로 변환된다.</p>
<pre><code>&lt;script&gt;
    const rawInput = prompt(&quot;inch 단위의 숫자를 입력해주세요.&quot;)
    const inch = Number(rawInput)
    const cm = inch * 2.54
    alert(`${inch}inch는 ${cm}cm입니다.`)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/565e48e8-b0bb-4e28-bae6-135fd6de2926/image.png" alt="">
사용자로부터 글자를 입력받고 Number로 형 변환 후 연산을 하는 과정이다.</p>
<h3 id="2-2-원의-반지름-입력받고-원의-넓이와-둘레-구하기">2-2) 원의 반지름 입력받고 원의 넓이와 둘레 구하기</h3>
<pre><code>&lt;script&gt;
    const input = prompt(&quot;반지름을 입력하세요.&quot;)
    const radius = Number(input)
    const area = 3.14 * radius * radius
    const round = 2 * 3.14 * radius
    alert(`넓이 : ${area}\n둘레 : ${round}`)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/c292651d-ff3b-445d-8c3c-225dd63e1527/image.png" alt=""></p>
<h3 id="2-3-환율-프로그램-usd---krw--1달러--1207원">2-3) 환율 프로그램 (USD -&gt; KRW) // 1달러 = 1207원</h3>
<pre><code>&lt;script&gt;
    const input = prompt(&quot;달러를 입력해주세요.&quot;)
    const dollar = Number(input)
    const won = dollar * 1207
    alert(`${dollar}달러는 ${won}원 입니다.`)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/b091dc68-313c-448b-b531-fdb08f9d47a8/image.png" alt=""></p>
<h3 id="2-4-환율-프로그램-eur---krw--1유로--143070원">2-4) 환율 프로그램 (EUR -&gt; KRW) // 1유로 = 1430.70원</h3>
<pre><code>&lt;script&gt;
    const input = prompt(&quot;유로를 입력해주세요.&quot;)
    const eur = Number(input)
    const won = eur * 1430.70
    alert(`${eur}유로는 ${won}원 입니다`)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/023ead64-1f6c-4027-be01-7b5aadda248b/image.png" alt=""></p>
<h1 id="3-정리">3. 정리</h1>
<p><strong>const</strong>는 입력되는 변수의 자료형에 따라 형이 정해진다.
<strong>prompt</strong>는 문자열을 입력받는 함수이다.
<strong>Number</strong>은 숫자형태의 문자를 숫자 자료형으로 바꿔주는 함수이다.
<strong>String</strong>은 문자 자료형으로 형 변환 시켜주는 함수이다.
<strong>confirm</strong>는 참, 거짓을 입력 받는 함수이다.
<strong>Boolean</strong>은 불 자료형으로 변환시켜준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 - 3]]></title>
            <link>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-3</link>
            <guid>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-3</guid>
            <pubDate>Sun, 27 Aug 2023 13:38:05 GMT</pubDate>
            <description><![CDATA[<h3 id="1-상수-선언">1. 상수 선언</h3>
<pre><code>&gt; const pi = 3.141592
&gt; pi</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/0667e466-50a0-4758-a69a-6882af6bfd80/image.png" alt=""></p>
<h3 id="2-변수-선언">2. 변수 선언</h3>
<pre><code>&gt; let pi = 3.141592
&gt; pi</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/3619c545-a6e1-4342-be66-b5fd0d021f9f/image.png" alt="">
상수는 변하지 않는 값으로 수정을 할 수 없고 변수는 변하는 값으로 수정을 할 수 있다.</p>
<h3 id="3-복합-대입-연산자-활용">3. 복합 대입 연산자 활용</h3>
<pre><code>&lt;script&gt;
    let list = &#39;&#39;

    list += &#39;&lt;ul&gt;&#39;
    list += &#39;   &lt;li&gt;Hello&lt;/li&gt;&#39;
    list += &#39;   &lt;li&gt;JavScript!&lt;/li&gt;&#39;
    list += &#39;&lt;ul&gt;&#39;

    document.write(list)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/e0fe2c5a-98fa-4663-ba8d-0bbda10026ed/image.png" alt="">
아래와 같이 작성하여도 똑같은 결과가 나온다.</p>
<pre><code>&lt;script&gt;
    const list = &#39;&lt;ul&gt;&lt;li&gt;Hello&lt;/li&gt;&lt;li&gt;JavaScript!&gt;&lt;/li&gt;&lt;ul&gt;&#39;
&lt;/script&gt;</code></pre><pre><code>&lt;script&gt;
    document.write(&#39;&lt;ul&gt;&lt;li&gt;Hello&lt;/li&gt;&lt;li&gt;JavaScript&lt;/li&gt;&lt;/ul&gt;&#39;)
&lt;/script&gt;</code></pre><h3 id="4-증감-연산자">4. 증감 연산자</h3>
<pre><code>&lt;script&gt;
    let number = 10
    alert(number++)
    alert(number++)
    alert(number++)
&lt;/script&gt;</code></pre><p>alert 창에 10,11,12가 순서대로 출력된다.</p>
<pre><code>&lt;script&gt;
    let number = 10
    alert(++number)
    alert(++number)
    alert(++number)
&lt;/script&gt;</code></pre><p>alert 창에 11,12,13이 순서대로 출력된다.</p>
<h3 id="5-undefined-자료형">5. undefined 자료형</h3>
<p>undefined 자료형은 상수나 변수로 선언되지 않은 식별자의 자료형을 나타낸다.
<img src="https://velog.velcdn.com/images/sot_sky/post/39e8cf14-874c-453a-84cb-11c562d397ea/image.png" alt=""></p>
<h3 id="6-문제">6. 문제</h3>
<h4 id="6-1">6-1)</h4>
<pre><code>&lt;script&gt;
    const r
    r = 10

    console.log(`넓이 = ${3.14 * r* r}`)
    console.log(`둘레 = ${2 * 3.14 * r}`)
&lt;/script&gt;</code></pre><p>코드 실행 시 발생하는 오류는 다음과 같다
<img src="https://velog.velcdn.com/images/sot_sky/post/ffc7bd13-82f7-4ce0-bf9c-2b6e5fe8084a/image.png" alt="">
상수로 선언된 r을 재정의하면서 발생하는 오류이다.</p>
<h4 id="6-2">6-2)</h4>
<pre><code>&lt;script&gt;
    let r
    r = 10

    console.log(`넓이 = ${3.14 * r * r}`)
    console.log(`둘레 = ${2 * 3.14 * r}`)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/a9422a68-c057-43cc-8f5e-9d5280aa557e/image.png" alt="">
let로 선언된 변수의 경우 재정의가 가능해 오류가 나지 않고 출력이 되고있다.
const에 비해 let는 자유로운 자료형이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 - 2]]></title>
            <link>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2</link>
            <guid>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2</guid>
            <pubDate>Thu, 24 Aug 2023 12:05:53 GMT</pubDate>
            <description><![CDATA[<h1 id="1-기본-용어">1. 기본 용어</h1>
<h3 id="1-1-표현식">1-1) 표현식</h3>
<p>표현식이란 하나의 표현을 나타내는 뜻으로 숫자 <code>245</code>, <code>10 + 20 + 30 * 2</code>, <code>var</code> 같은 것을 말한다.</p>
<h3 id="1-2-문장">1-2) 문장</h3>
<p>문장이란 하나 이상의 표현식이 모여서 만들어지는 것으로 문장을 구분할 때는 <code>;</code>또는 <code>줄바꿈</code>으로 종결을 알려준다.</p>
<pre><code>10 + 20 + 30 * 2;var name = &#39;sim&#39; + &#39;curity&#39;;alert(&#39;Hello JavaScript&#39;);</code></pre><h3 id="1-3-키워드">1-3) 키워드</h3>
<p>키워드란 처음부터 특별한 의미가 있는 단어를 나타낸다.
<code>await</code>, <code>break</code>, <code>case</code>, <code>catch</code>, <code>class</code> 등이 있다.</p>
<h3 id="1-4-식별자">1-4) 식별자</h3>
<p>식별자란 변수명, 함수명을 사용하기 위한 용어로 몇가지 규칙이 있다.</p>
<pre><code>키워드를 사용하면 안된다.
숫자로 시작하면 안된다.
특수 문자는 _와 $만 허용한다.
공백 문자를 사용할 수 없다.</code></pre><h3 id="1-5-주석">1-5) 주석</h3>
<p>html에서 주석은 <code>&lt;!-- --&gt;</code>로 나타낸다. 
자바스크립트에서는 <code>//</code>로 한줄 주석처리를 할 수 있다.
또한, <code>/*&lt;script&gt;&lt;/script&gt;*/</code>로 여러 줄 주석 처리를 할 수 있다.</p>
<h1 id="2-출력">2. 출력</h1>
<h3 id="2-1-표현식-출력">2-1) 표현식 출력</h3>
<p>크롬 브라우저 개발자 도구에서 하단에 console 창을 열고 값을 입력하면 표현식이 출력된다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7eef9ec2-9358-4e50-b6c3-bf1586118121/image.png" alt=""></p>
<h3 id="2-2-alert-창-출력">2-2) alert 창 출력</h3>
<p>Vscode에서 다음과 같이 작성한 후 파일을 열어본다.</p>
<pre><code>&lt;script&gt; 
    alert(&#39;Hello World&#39;)
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/5f914406-e1de-467b-af6a-e4f78eca5a0d/image.png" alt="">
alert 창에 작성한 문구가 출력된다.</p>
<h3 id="2-3-console-창-출력">2-3) console 창 출력</h3>
<p>Vscode에서 다음과 같이 코드를 작성하고 파일을 열어본다.</p>
<pre><code>&lt;script&gt;
    console.log(&#39;Hello World&#39;)
&lt;/script&gt;</code></pre><p>알림 창이 뜨지 않지만 개발자 도구 console 창을 확인하면 문구가 떠있는 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/sot_sky/post/6089f7b4-0649-446f-b23f-3d2ff0d0efb0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 - 1]]></title>
            <link>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-1</link>
            <guid>https://velog.io/@sot_sky/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-1</guid>
            <pubDate>Thu, 24 Aug 2023 11:31:04 GMT</pubDate>
            <description><![CDATA[<h1 id="1-javascript란">1. JavaScript란?</h1>
<p>자바스크립트란 웹 페이지에서 동작하는 언어로 동적인 행위를 수행해준다. 
웹 페이지에서 서버로 요청이 갈때 첫 번째로 클라이언트 단에서 검증을 한다.
그 과정에서 자바스크립트가 쓰이고 그 후 서버에서 한번 더 검증을 하는 방식으로 현재 웹 보안이 이루어지고 있다.
그러므로, 웹 취약점 진단을 수행할 때 자바스크립트를 알아야 클라이언트 단에서 일어나는 검증과정과 서버 단에서 일어나는 검증 과정을 명확하게 구분지을 수 있다.</p>
<h3 id="1-1-개발환경-구성">1-1. 개발환경 구성</h3>
<p>Vscode와 Chrome 브라우저를 활용하여 공부를 할 것이다.
<a href="https://code.visualstudio.com/download">https://code.visualstudio.com/download</a> Vscode 설치 링크
<a href="https://www.google.co.kr/chrome/?brand=FHFK&amp;gclid=Cj0KCQjw_5unBhCMARIsACZyzS3aRLkknG6DeA0TyAXa2X5ofBwjoQ-8o1Ss5TM-4XtdG40pejQ23mUaAhm3EALw_wcB&amp;gclsrc=aw.ds">https://www.google.co.kr/chrome/?brand=FHFK&amp;gclid=Cj0KCQjw_5unBhCMARIsACZyzS3aRLkknG6DeA0TyAXa2X5ofBwjoQ-8o1Ss5TM-4XtdG40pejQ23mUaAhm3EALw_wcB&amp;gclsrc=aw.ds</a> Chrome 설치 링크
다 설치하였으면 Vscode 실행 후 Korean 플러그인 설치를 한다.
<img src="https://velog.velcdn.com/images/sot_sky/post/fd0760b2-29ac-4266-94c7-9d9fb7fdc157/image.png" alt="">
재시작하면 한글로 언어가 변경되어있다.</p>
<h1 id="2-실습">2. 실습</h1>
<p>파일 &gt; 새 파일 클릭 후 test.html로 파일을 만들어 준다.
&#39;html&#39; 코드를 입력하면 밑에 자동완성에 html:5가 생기고 선택을 하면 자동으로 코드가 완성된다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>우리는 자바스크립트 공부가 목적이므로 불필요한 것들은 모두 지워주고 자바스크립트를 추가해준다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;title&gt;&lt;/title&gt;
    &lt;script&gt;
        alert(&quot;hello JavaScript!!!&quot;)
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>이제 파일을 저장해주고 해당 디렉터리로 들어가면 저장한 파일이 있다.
<img src="https://velog.velcdn.com/images/sot_sky/post/4e42ff8c-5b95-4e10-af7f-10c51ff64fc2/image.png" alt="">
파일을 실행해주면 작성한 자바스크립트 코드가 실행된다.
<img src="https://velog.velcdn.com/images/sot_sky/post/80aa8c43-5715-4ba1-bbc3-e64e86e23f01/image.png" alt=""></p>
<h1 id="3-오류">3. 오류</h1>
<p>자바스크립트를 공부하다보면 다양한 오류를 만나게 될것이다.
작성한 코드에서 alert()함수를 alrt로 오타로 수정한 후 실행해보자.
alert 창은 뜨지 않고 흰 배경만 나온다.
배경을 우클릭 한 후 검사를 눌러보자.
<img src="https://velog.velcdn.com/images/sot_sky/post/17b05f6b-271c-44d6-b971-409d035cde7f/image.png" alt="">
다음과 같이 개발자 도구 창이 켜질 것이다.
우측 위 빨간 동그라미는 자바스크립트에서 오류가 발생했다는 뜻이고 이를 클릭해보자.
<img src="https://velog.velcdn.com/images/sot_sky/post/d23f112d-54ee-4863-bafe-89bcd3415da9/image.png" alt="">
다음과 같이 에러가 발생한 코드의 위치를 알려준다. 
6번째 줄에서 에러가 발생했다는 뜻이고 클릭해주면 코드로 이동을 한다.
<img src="https://velog.velcdn.com/images/sot_sky/post/3737bda4-54ea-46f4-99fc-68f6727418ce/image.png" alt="">
이 말고도 자주 만나게 되는 오류들은 여러가지가 있다.</p>
<h3 id="1-uncaught-referenceerror">1) Uncaught ReferenceError</h3>
<pre><code>&lt;script&gt;
    alrt(&#39;Hello JavaScript&#39;)
&lt;/script&gt;</code></pre><p>방금처럼 alert를 alrt로 오타가 났을때 발생하는 에러로 발생한 부분이 is not defined 되었다고 알려준다.</p>
<h3 id="2-uncaught-syntaxerror--invalid-or-unexcepted-token">2) Uncaught SyntaxError : Invalid or unexcepted token</h3>
<pre><code>&lt;script&gt;
    alert(&#39;Hello JavaScript)
&lt;/script&gt;</code></pre><p>이처럼 문자열에 따옴표를 닫아주지 않았을 때 발생하는 오류이다.</p>
<h3 id="3-uncaught-syntaxerror--missing--after-argument-list">3) Uncaught SyntaxError : missing ) after argument list</h3>
<pre><code>&lt;script&gt;
    alert(&#39;Hello JavaScript&#39;
&lt;/script&gt;</code></pre><p>괄호가 제대로 닫히지 않았을 때 발생하는 오류이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 앱 취약점 진단]]></title>
            <link>https://velog.io/@sot_sky/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EC%B7%A8%EC%95%BD%EC%A0%90-%EC%A7%84%EB%8B%A8-5h4cuukh</link>
            <guid>https://velog.io/@sot_sky/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EC%B7%A8%EC%95%BD%EC%A0%90-%EC%A7%84%EB%8B%A8-5h4cuukh</guid>
            <pubDate>Mon, 12 Jun 2023 10:39:06 GMT</pubDate>
            <description><![CDATA[<h1 id="1-드로저를-활용한-취약점-진단">1. 드로저를 활용한 취약점 진단</h1>
<p>드로저는 모바일 애플리케이션 취약점 진단 프레임워크인 머큐리의 새로운 업데이트 버전입니다.</p>
<h3 id="1-1-설치">1-1) 설치</h3>
<p><a href="https://labs.withsecure.com/tools/drozer">https://labs.withsecure.com/tools/drozer</a> 에서 apk only와 msi를 다운받습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ff1ad4c7-5cff-41e8-be2d-0145477c8c65/image.png" alt="">
msi파일은 윈도우에서 설치 후 apk파일도 녹스 기기에 설치해줍니다.
녹스에서 실행 후 on으로 바꿔줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/0dd6b251-9c41-4beb-ba66-e93063727952/image.png" alt="">
그 후 윈도우에서 다음 명령을 입력해줍니다.</p>
<blockquote>
<p>adb forward tcp:31415 tcp:31415
drozer.bat console connect
<img src="https://velog.velcdn.com/images/sot_sky/post/46025b8a-8d50-4db2-96f3-cdd2eb33cec3/image.png" alt="">
오류가 발생하는데 이는 모듈이 없어서 발생하는 오류입니다. 필요한 모듈을 다운 받아줍니다.
필요 모듈 : protobuf, pyopenssl, twisted</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/73bad2e3-7bcd-48c8-9334-7dfa90207452/image.png" alt="">
성공적으로 실행됬네요.</p>
<h1 id="2-드로저를-활용한-앱-패키지-정보-확인">2. 드로저를 활용한 앱 패키지 정보 확인</h1>
<p>이제 드로저를 이용해 앱의 기본적인 정보들로부터 취약점을 확인하고 취약점으로부터 어떤 정보를 얻을 수 있는지 살펴보겠습니다.</p>
<blockquote>
<p>run app.package.list -f insecure
<img src="https://velog.velcdn.com/images/sot_sky/post/0d18eb4b-4ee7-4e19-8b88-4064dc7e8077/image.png" alt=""></p>
</blockquote>
<p>insecurebank의 패키지 정보를 확인했습니다.</p>
<blockquote>
<p>run app.package.info -a com.android.insecurebankv2
<img src="https://velog.velcdn.com/images/sot_sky/post/821c525f-f9f8-479c-928a-268440717e8c/image.png" alt=""></p>
</blockquote>
<p>인시큐어뱅크의 패키지 정보를 확인했습니다.</p>
<p>여러 정보가 나옵니다. 프로세스 정보, 데이터 저장 위치, apk 파일 위치, UID 특히나 사용하는 권한이 눈에 띄네요.</p>
<blockquote>
<p>run app.package.info -p &lt;특정 권한&gt;</p>
</blockquote>
<p>입력으로 특정 권한을 가지고 있는 패키지 목록을 확인할 수 있습니다.
예를 들어, <code>android.permission.SEND_SMS</code> 권한은 SMS를 보낼수 있는 권한으로 이러한 권한은 AndroidManifest.xml파일에 정의되어 있고 모든 앱의 최상위 폴더에 존재합니다. 또한, 여러 정보를 추출할 수 있어 기본적으로 분석 대상입니다.</p>
<p>다음으로는 manifest파일을 볼 수 있는 명령어입니다.</p>
<blockquote>
<p>run app.package.manifest &lt;패키지 명&gt;
<img src="https://velog.velcdn.com/images/sot_sky/post/4160031c-5cf8-4a4b-9dac-073dd230e279/image.png" alt="">
앞서 보았던 권한들이 그대로 출력됩니다. 이처럼 앱의 기본적인 정보들을 추출하는 것만으로도 취약한 부분을 확인할 수 있고 유추할 수 있기 때문에 분석에 앞서 기본적인 정보들을 수집하는 것이 좋습니다.</p>
</blockquote>
<h1 id="3-드로저를-이용한-취약점-분석">3. 드로저를 이용한 취약점 분석</h1>
<p>드로저는 자동으로 앱의 취약한 부분을 검색하는 기능을 가지고 있습니다.</p>
<blockquote>
<p>run app.package.attacksurface &lt;패키지 명&gt;</p>
</blockquote>
<p>명령어로 분석할 수 있습니다.</p>
<p>인시큐어 뱅크 앱을 분석해 보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/c8577eac-9715-4165-96cc-047201d58910/image.png" alt="">
결과로 액티비티에서 5개, 브로드캐스트 리시버에서 1개, 컨텐츠 프로바이더에서 1개 그리고 디버깅이 가능한 취약점이 발견되었습니다.</p>
<h3 id="3-1-액티비티-분석">3-1) 액티비티 분석</h3>
<p>5개의 액티비티가 노출된 정보를 더 자세하게 알아보기 위해 다음 명령어를 입력합니다.</p>
<blockquote>
<p>run app.activity.info -a &lt;패키지 명&gt;
<img src="https://velog.velcdn.com/images/sot_sky/post/c0914d44-4a6f-4ce5-b2b6-b2d3872b0c31/image.png" alt="">
취약한 액티비티 정보가 정확하게 나왔습니다.</p>
</blockquote>
<p>이제 실제로 액티비티를 실행시킬 수 있는지 테스트 해보겠습니다.
changePassword 액티비티를 정상적으로 로그인 후 접근했을 때와 드로저를 이용해 비정상적으로 접근했을 때의 차이점을 살펴보겠습니다.</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/8b689c10-0073-4405-8175-346a4949b8cf/image.png" alt="">
정상적인 접근 시 계정의 ID가 적혀져 있습니다.</p>
</blockquote>
<blockquote>
<p>run app.activity.start --component com.android.insecurebankv2 com.android.insecurebankv2.ChangePassword
위 명령어를 한 문장으로 입력하게 되면
<img src="https://velog.velcdn.com/images/sot_sky/post/06007105-a9c4-46a1-9677-eb5508a2dceb/image.png" alt="">
ID는 적혀있지 않은 액티비티가 노출됩니다. 즉 비정상적인 접근의 경우 로그인 정보가 없어 ID 항목이 공란이 됩니다.</p>
</blockquote>
<p>&amp;nbsp</p>
<h3 id="3-2-브로드캐스트-리시버-분석">3-2) 브로드캐스트 리시버 분석</h3>
<p>브로드캐스트 리시버는 디바이스에서 이벤트가 발생하는 경우 브로드캐스트 신호를 주고받으며 시스템상에 일어나는 상황들을 공유합니다. 신호를 받기위해 각각의 신호에 맞는 액션 역시 적용되어 있어야 합니다. 인시큐어뱅크의 정의되어 있는 정보를 확인해보겠습니다.</p>
<blockquote>
<p>run app.broadcast.info -f insecure
<img src="https://velog.velcdn.com/images/sot_sky/post/b19ad9a7-f708-429b-be7c-7bfdd45148c1/image.png" alt=""></p>
</blockquote>
<p>입력 시 앞서 본 브로드캐스트 취약한 부분이 상세하게 출력됩니다.
인시큐어뱅크 앱에서 사용하는 브로드캐스트 리시버 이름은 <code>MyBroadCastReveiver</code>입니다.
app.broadcast.info 모듈에 여러 옵션이 있습니다.
-a : 특정 패키지에 대해 브로드캐스트 취약점 점검
-f : 특정 단어를 포함한 앱만 취약점 점검
-u : 숨겨진 리시버를 점검
<img src="https://velog.velcdn.com/images/sot_sky/post/e9929151-dbb9-4393-8263-41963d2661fe/image.png" alt="">
숨어있는 리시버가 있는 경우 다음과 같이 hidden reveiver이 출력됩니다.</p>
<h3 id="3-3-컨텐츠-프로바이더-분석">3-3) 컨텐츠 프로바이더 분석</h3>
<p>컨텐츠 프로바이더는 특정 애플리케이션이 사용하고 있는 데이터베이스를 공유하기 위해 사용됩니다. 또한, 전체 데이터가 아닌 공유하고 싶은 데이터만 공유를 할 수도 있습니다.</p>
<blockquote>
<p>run app.provider.info -a &lt;패키지 명&gt;
<img src="https://velog.velcdn.com/images/sot_sky/post/68c363af-29f7-4d0b-b546-6774bb3c0de9/image.png" alt="">
인시큐어뱅크의 프로바이더 정보입니다.</p>
</blockquote>
<p>결과로 <code>com.android.insecurebankv2.TrackUserCintentProvider</code>이 노출되었습니다.
프로바이더는 데이터베이스에 있는 정보를 URI로 공유하기 때문에 데이터베이스에 접근하기 위해서는 URI 정보가 반드시 필요합니다.</p>
<blockquote>
<p>run app.provider.finduri &lt;패키지 명&gt;
<img src="https://velog.velcdn.com/images/sot_sky/post/15b3a917-b1c8-45da-9d5e-fb4975acf724/image.png" alt="">
앱의 URI 정보를 얻었습니다. 이제 이 정보로 어떤 정보에 접근할 수 있는지 확인해보겠습니다.</p>
</blockquote>
<blockquote>
<p>run scanner.provider.sqltables --uri content://com.android.insecurebankv2.TrackUserContentProvider/trackusers/
<img src="https://velog.velcdn.com/images/sot_sky/post/b18a5d97-6ffe-4956-8221-88fe814130a9/image.png" alt="">
3가지 테이블에 접근할 수 있습니다.</p>
</blockquote>
<blockquote>
<p>run scanner.provider.sqltables -a com.android.insecurebankv2
<img src="https://velog.velcdn.com/images/sot_sky/post/b05ed705-a865-45a7-bfd2-3513b64ca95e/image.png" alt="">
-a 옵션으로 패키지에서 사용하는 모든 URI 정보를 얻을 수 있습니다.</p>
</blockquote>
<p>이제 얻은 테이블 정보를획득하기 위해 app.provider.query 모듈을 사용하겠습니다.
먼저, -h로 모듈 설명을 보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/9632a7a1-c815-4379-80f0-366bbf435011/image.png" alt="">
모듈 뒤에 uri만 넣음으로써 자동으로 분석하는 방법이 있고 --selection, --projection 옵션을 이용해 데이터베이스 정보를 추출하는 방법도 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7097e446-c5d1-41a7-b280-0784dd089bfc/image.png" alt="">
uri만 넣으니 로그인한 순서가 보여집니다.</p>
<p>다른 정보도 얻기 위해 sqlite_master 테이블을 사용합니다. 안드로이드는 기본적으로 sqlite 데이터베이스를 사용합니다.</p>
<blockquote>
<p>run app.provider.query content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers --projection &quot;* FROM SQLITE_MASTER WHERE type = &#39;table&#39;;--&quot;</p>
</blockquote>
<p>리눅스나 윈도우에서의 sql injection 처럼 쿼리문에 변화를 주어 정보를 추출합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/47d15f7a-c22f-4570-9847-52a5427596b9/image.png" alt="">
--projection 옵션으로 작성한 sql 쿼리문이 적용되었고 그로 인해 sqlite_master 정보가 노출되었습니다.
그럼 이제 더 구체적인 정보를 얻기위해 쿼리문을 바꿔보겠습니다.</p>
<blockquote>
<p>run app.provider.query content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers --projection name&#39; from names where id = 2;&#39;--
<img src="https://velog.velcdn.com/images/sot_sky/post/71b3e573-f257-422a-88d9-cfbdb2432e5a/image.png" alt="">
id 컬럼의 값이 2인 names을 추출하였습니다.</p>
</blockquote>
<p>이를 통해 알 수 있는 것은 컨텐츠 프로바이더에 관련된 정보를 얻고 URI 정보를 얻은 후 URI 정보를 기반으로 쿼리문을 생성하여 sql 인젝션 공격이 가능하다는 것입니다.</p>
<p><code>scanner.provider.injection</code> 모듈은 이러한 인젝션 공격이 가능한지 취약점 여부를 판단해주는 모듈입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f0992408-27a4-47b3-ab70-e52942392b79/image.png" alt="">
취약한 URI 주소와 인젝션 공격 방법도 분류해줍니다.</p>
<h3 id="3-4-서비스-분석">3-4) 서비스 분석</h3>
<p>서비스란 액티비티와 반대로 UI가 없이 백그라운드에서 실행되는 컴포넌트의 한 종류입니다.
예시로, 음악이 있습니다. 음악을 틀고 다른 어플을 실행해도 백그라운드에서 동작하는 서비스가 실행되기 때문에 음악은 정지하지 않습니다.</p>
<blockquote>
<p>run app.service.info -a &lt;패키지 명&gt;
<img src="https://velog.velcdn.com/images/sot_sky/post/dfe40d0c-a253-4f30-bc24-ec33d48735e6/image.png" alt="">
bluetooth 앱의 서비스 목록 입니다. 해당 모듈도 마찬가지로 -u, -f옵션이 있습니다.</p>
</blockquote>
<blockquote>
<p>run app.service.info -u -f com.android.bluetooth
<img src="https://velog.velcdn.com/images/sot_sky/post/ae65eefa-ff03-4853-9df4-3426b7b62df4/image.png" alt=""></p>
</blockquote>
<h3 id="3-5-debuggable-package-분석">3-5) Debuggable package 분석</h3>
<p>마지막으로 발견된 디버깅 가능 취약점입니다. 디버깅을 통해 중요 정보를 노출할 수 있으며 액티비티 우회 취약점, 컨텐츠 프로바이더 실행 취약점 등이 이에 해당합니다.</p>
<blockquote>
<p>run app.package.debuggable -f insecure
<img src="https://velog.velcdn.com/images/sot_sky/post/aa8cc4b6-4b43-41bb-b926-01500b9a8d40/image.png" alt="">
인시큐어뱅크 앱의 디버깅이 가능한 권한 목록들입니다.</p>
</blockquote>
<h1 id="4-모듈-관리">4. 모듈 관리</h1>
<p>드로저는 모듈을 기반으로 작동하는 취약점 진단 서비스입니다.
그러므로 다양한 모듈들을 추가하거나 삭제할 수 있습니다.</p>
<h3 id="4-1-모듈-설치">4-1) 모듈 설치</h3>
<p>저장소를 관리하는 방법에는 크게 두 가지가 있습니다.
search 명령어로 연결된 공식 모듈 저장소로부터 다운로드할 수 있는 모듈을 검색해보겠습니다.</p>
<blockquote>
<p>module search
<img src="https://velog.velcdn.com/images/sot_sky/post/0bfe4188-3252-4d7e-b547-9db2ba5f9ae7/image.png" alt="">
module search root -d exploit
<img src="https://velog.velcdn.com/images/sot_sky/post/456f27a4-d279-4924-8657-ed36aed88e6f/image.png" alt="">
모듈이름에 root가 들어가고 exploit 기능이 있는 모듈들을 출력합니다.
module install metall0id.root.cmdclient
<img src="https://velog.velcdn.com/images/sot_sky/post/7b24af56-7db7-4fc0-bf06-1cfc614fb131/image.png" alt="">
모듈을 설치할 경로를 입력하면 모듈이 성공적으로 설치됩니다.</p>
</blockquote>
<p>다음 시간엔 본격적으로 취약점 항목별 실습을 진행하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 앱 취약점 진단]]></title>
            <link>https://velog.io/@sot_sky/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EC%B7%A8%EC%95%BD%EC%A0%90-%EC%A7%84%EB%8B%A8</link>
            <guid>https://velog.io/@sot_sky/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EC%B7%A8%EC%95%BD%EC%A0%90-%EC%A7%84%EB%8B%A8</guid>
            <pubDate>Mon, 12 Jun 2023 06:11:21 GMT</pubDate>
            <description><![CDATA[<h1 id="1-로그캣-확인">1. 로그캣 확인</h1>
<p>로그캣은 안드로이드 장치에서 발생하는 로그 메시지를 화면으로 출력하거나 파일 형태로 저장해주는 기능을 합니다. 안드로이드 로깅 시스템은 시스템 디버그 출력 정보를 확인하거나 수집할 수 있는 메커니즘을 제공합니다.
로그캣 명령어는 다음과 같습니다.</p>
<blockquote>
<p>adb logcat [option]</p>
</blockquote>
<p>또한, 기기의 쉘 구동 후 logcat을 입력해 실행하는 방법도 있습니다.
로그캣 수행 시 
<img src="https://velog.velcdn.com/images/sot_sky/post/430fb43a-20d1-4710-b255-09b186e59e6c/image.png" alt="">
이렇게 기기에서 발생하는 로그 메시지를 출력해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/5a9b892b-f2e2-4f71-b0c1-51855fba574b/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/86f1c707-74f4-4d48-8ce4-d87e769f8bab/image.png" alt="">
전송을 하자 기기에 입력한 변수들도 출력이 됩니다.</p>
<h3 id="1-1-우선순위">1-1) 우선순위</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/5bf37ed4-0682-4d46-ad5c-d68ac0fdad45/image.png" alt="">
표시한 부분을 보면 영어 대문자 하나가 써져있는데 이들이 의미하는 것은 우선순위로 종류는 다음과 같습니다.</p>
<blockquote>
<p>V - Verbose : 일반적인 수준
D - Debug : 
I - Info
W - Waring
E - Error
F - Fatal
S - Silent : 가장 높은 우선 순위</p>
</blockquote>
<p>리눅스의 syslog 위험 수준과 비슷한것 같네요.</p>
<h3 id="1-2-logcat-옵션">1-2) logcat 옵션</h3>
<p><strong>-b : 로그 내용을 선택적으로 출력 할 수 있게 해줍니다.</strong>
adb logcat -b &lt;radio | events | main&gt;
<img src="https://velog.velcdn.com/images/sot_sky/post/0d0a8ae5-c99d-48f2-9e0b-c5be964eac68/image.png" alt="">
events로 기록 중에 위에 상태바를 열고 닫으니 notiffication_panel이 revealed, hidden됐다고 로깅되네요.</p>
<p><strong>-c : 기록된 로그 메시지를 삭제하고 종료합니다.</strong></p>
<p><strong>-d : 로그 메시지를 화면에 덤프하고 종료합니다.</strong></p>
<p><strong>-f <filename> : 로그 메시지를 지정한 파일 이름으로 저장합니다.</strong>
adb -d -f /sdcard/output.txt -b radio
입력 후 해당 파일을 열어보니 
<img src="https://velog.velcdn.com/images/sot_sky/post/92055a58-528c-4ac9-95e3-78a98cb425f6/image.png" alt="">로그가 저장되어 있었습니다.</p>
<p><strong>-g : 선택된 버퍼의 크기를 출력하고 종료합니다.</strong>
<img src="https://velog.velcdn.com/images/sot_sky/post/599adfaa-bef2-4365-948c-79699f797490/image.png" alt=""></p>
<p><strong>-n : 저장되는 로그 파일의 개수를 지정합니다. 이 때 -r옵션으로 로그 파일 용량을 설정하고 -f로 파일 이름을 지정해야 합니다.</strong>
<img src="https://velog.velcdn.com/images/sot_sky/post/188a1d81-61e1-420f-b298-2f0b7d2d5262/image.png" alt="">
용량이 찰 때까지 기다리는 거 같습니다.</p>
<p><strong>-r : 로그 메시지를 파일로 저장 시 저장되는 파일의 용량을 설정합니다. 기본 값은 16입니다.</strong></p>
<p><strong>-s : 기본 필터의 종류를 s로 변경시켜 모든 우선순위 메세지를 조용히시키는 것으로 모든 메시지를 출력하지 않도록 합니다. 뒤에 옵션을 붙임으로서 사용자가 원하는 특정 로그 메시지를 확인할 수 있습니다.</strong></p>
<p><strong>-v : 로그 메시지는 메타데이터 필드와 태그, 우선순위를 갖습니다. v옵션으로 특정 메타데이터를 출력할 수 있습니다.</strong></p>
<ul>
<li>brief : 로그 메시지의 우선순위/태그, 메시지 발생한 프로세스의 PID 출력</li>
<li>process : 로그 메시지의 발생한 PID 출력</li>
<li>tag : 로그 메시지의 우선순위/태그 출력</li>
<li>time: 로그 메시지의 날짜, 호출 시간, 우선순위/태그, PID 출력</li>
</ul>
<blockquote>
<p>adb logcat ActivityManager:I art:I *:S</p>
</blockquote>
<p>위 명령은 ActivityManager와 art태그 중 Info나 그 이상의 우선순위를 갖는 항목을 출력해주고 나머지 로그들은 S옵션으로 출력하지 않는 명령입니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/aa00f9c2-2aad-4cb8-a684-e9a0f2f4318d/image.png" alt="">
다음과 같이 출력되네요.</p>
<h1 id="2-bugreport">2. bugreport</h1>
<p>  bugreport 명령은 dumpsys, dumpstate, logcat 명령의 결과를 한 번에 출력해줍니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/8d011704-afb6-4b3b-a84d-efe6594ecba9/image.png" alt="">
너무 크다보니 압축 형식으로 저장을 해주는군요.</p>
<h3 id="2-1-dumpsys">2-1) dumpsys</h3>
<p>  현재 연결된 안드로이드 장치의 애플리케이션 및 장치 정보를 자세히 표시해줍니다
부가적인 옵션없이 사용할 경우 매우 많은 정보가 출력되어 확인하기 어려워 추가 옵션을 줍니다.</p>
<ul>
<li>meminfo</li>
<li>cpuinfo</li>
<li>activity</li>
<li>account
등의 옵션이 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/65ff5ab1-5fe2-4513-a4d8-bf7398b503a8/image.png" alt="">
adb shell dumpsys meminfo 명령 사용 결과</li>
</ul>
<h3 id="2-2-dumpstate">2-2) dumpstate</h3>
<p>  현재 연결된 안드로이드 장치의 모든 상태 정보를 출력해줍니다.</p>
<h3 id="2-3-logcat">2-3) logcat</h3>
<p>  안드로이드 시스템에서 발생하는 로그 정보를 출력해줍니다.</p>
<h1 id="3-jdwp">3. jdwp</h1>
<p>  jdwp 명령은 연결된 안드로이드 장치에서 사용할 수 있는 jdwp 프로세스의 목록을 출력합니다.
  명령 입력 시 특정 애플리케이션의 프로세스 번호도 출력이 되는데 이를 이용해 디버깅에 사용할 수 있습니다.
  애플리케이션의 디버깅을 수행한다는 가정하에 jdwp 프로세스 번호를 알아보겠습니다.
  처음엔 아무것도 안뜨다가 인시큐어뱅크를 실행하고 명령어를 쳐보니
  <img src="https://velog.velcdn.com/images/sot_sky/post/f12ea21e-b4e1-481e-90fb-0c6d65b4a7a0/image.png" alt="">
다음과 같이 하나의 프로세스가 나타났습니다.</p>
<h1 id="4-installuninstall-pullpush">4. install/uninstall, pull/push</h1>
<h3 id="4-1-install">4-1) install</h3>
<p>  install 명령으로 특정 애플리케이션을 설치할 수 있습니다.
  하지만, 이미 설치된 경우 다음과 같이 오류가 발생합니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/f3c8b52d-52c0-455b-805b-704aa4e81638/image.png" alt="">
-r 옵션을 사용하면 기존 데이터의 삭제 없이 애플리케이션이 재설치됩니다.</p>
<h3 id="4-2-uninstall">4-2) uninstall</h3>
<p>  uninstall 명령으로 설치된 애플리케이션을 삭제할 수 있습니다.</p>
<blockquote>
<p>adb uninstall &lt;설치된 패키지 이름&gt;</p>
</blockquote>
<p>  형식으로 입력을 해야하는데 설치된 패키지 이름을 알기 위해 다음 명령을 쓸 수 있습니다.</p>
<blockquote>
<p>adb shell pm list packages -f</p>
</blockquote>
<p>  여기서 pm은 package manager로 패키지 목록을 보여줍니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/76009fd7-653f-44e8-a466-2131e50c3bfb/image.png" alt="">
인시큐어뱅크의 패키지 파일을 찾았습니다.
  com.android.insecurebankv2입니다.</p>
<p>  <img src="https://velog.velcdn.com/images/sot_sky/post/6c0318db-ffc9-4c29-99df-f6e0b6a34e18/image.png" alt="">
uninstall 명령으로 삭제했다가
  <img src="https://velog.velcdn.com/images/sot_sky/post/1bc3accc-9e36-4954-af61-3e04dfa408ed/image.png" alt="">
다시 설치해보았습니다.</p>
<h3 id="4-3-pull">4-3) pull</h3>
<p>  pull 명령은 연결된 에뮬레이터나 장치에 저장된 파일을 PC로 복사하는 기능을 수행합니다.</p>
<blockquote>
<p>adb pull &lt;장치 경로/파일&gt; &lt;저장할 위치&gt;</p>
</blockquote>
<p>  형식으로 명령합니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/676d8263-cd7b-4617-a862-b30ceb06127c/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/1328c080-c29b-4080-aff8-c847ef4d104b/image.png" alt="">
PC로 파일을 저장했습니다.</p>
<h3 id="4-4-push">4-4) push</h3>
<p>  push 명령은 반대로 PC에서 안드로이드 에뮬레이터로 파일을 저장시키는 명령어입니다.</p>
<blockquote>
<p>adb push &lt;파일 위치&gt; &lt;저장할 위치&gt;</p>
</blockquote>
<p>  <img src="https://velog.velcdn.com/images/sot_sky/post/6e34c7f6-4b6e-452a-b678-22df910e2578/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/9fcbb894-1976-4d58-8a39-544cd7f5af0a/image.png" alt="">
파일이 저장되었습니다.</p>
<h1 id="5-forward">5. forward</h1>
<p>  forward 명령은 특정 로컬 포트를 안드로이드 장치의 틀정 포트와 소켓 통신이 가능하도록 포워딩해주는 기능을 합니다.</p>
<blockquote>
<p>forward &lt;로컬 포트&gt; &lt;원격지 포트&gt;</p>
</blockquote>
<p>  형식으로 작성하며 이 명령을 사용하기 위해선 대상 장치에서 USB 디버깅 옵션이 활성화되어야 합니다.
  인시큐어 뱅크를 jdwp와 forward를 이용해 디버깅 환경을 구성해보겠습니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/0fa0b992-801e-494d-964c-cddabac58a83/image.png" alt="">
인시큐어뱅크의 PID입니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/a8346611-c97c-4f9f-80e7-5238f64b85f4/image.png" alt="">
이제 안드로이드의 jdwp 7075에 포워딩 상태입니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/df18ad05-c3b3-44b5-860c-15007237b668/image.png" alt="">
다음으로 jdb를 통해 애플리케이션에 대한 디버깅이 가능한 상태로 만들어주면 디버깅이 가능합니다.</p>
<h1 id="6-스크립팅-기능">6. 스크립팅 기능</h1>
<p>  스크립트 범주는 사용자에 의해 제작되는 스크립트에 연결된 장치 시리얼 번호나 기기 정보와 같은 특정한 정보 등을 출력하는 데 사용할 수 있습니다.</p>
<h3 id="6-1-get-serialno">6-1) get-serialno</h3>
<p>  연결된 장치의 시리얼 번호를 문자열로 출력합니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/003ab1bd-12d4-46a6-8072-274b4058b3e8/image.png" alt=""></p>
<h3 id="6-2-get-state">6-2) get-state</h3>
<p>  연결된 안드로이드 에뮬레이터나 장치의 상태를 문자열로 출력합니다.
  <img src="https://velog.velcdn.com/images/sot_sky/post/46bceaf9-4559-488d-9fe5-7783a2760a4b/image.png" alt=""></p>
<h3 id="6-3-wait-for-device">6-3) wait-for-device</h3>
<p>  연결되는 에뮬레이터나 장치가 구동될 때까지 adb를 통한 명령 실행을 멈춰 놓고 장치가 device 상태가 되면 명령과 함게 설정된 명령어를 실행합니다.</p>
<blockquote>
<p>adb wait-for-device &lt;명령어&gt;</p>
</blockquote>
<p>  형식으로 구성됩니다.</p>
<h1 id="7-서버-기능">7. 서버 기능</h1>
<p>  현재 ADB 서버의 상태를 확인하고 통신에 문제가 발생 시 서버를 재시작하는 등의 역할을 수행합니다.</p>
<h3 id="7-1-start-server">7-1) start-server</h3>
<p>  ADB 서버 프로세스가 동작하는지의 여부를 확인한 후 결과를 표시합니다.
  만약 서버가 동작하지 않는 상태라면 서버를 구동시킵니다.</p>
<h3 id="7-2-kill-server">7-2) kill-server</h3>
<p>  ADB 이용 도중 서버에 문제가 발생하거나 상태가 이상한 경우 서버를 종료시키는 역할을 합니다. 후에 다른 adb 명령 입력 시 서버를 다시 시작합니다.</p>
<p>  <img src="https://velog.velcdn.com/images/sot_sky/post/4efdcaf4-428c-47f9-84a2-92c99bb696b7/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 앱 취약점 진단]]></title>
            <link>https://velog.io/@sot_sky/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@sot_sky/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Fri, 09 Jun 2023 06:56:34 GMT</pubDate>
            <description><![CDATA[<p>공부를 위해 &#39;안드로이드 모바일 앱 모의해킹 (저자 : 조정원, 김영근, 조승현, 류진영, 김광수)&#39; 책을 구매하여 책을 기반으로 공부하겠습니다.</p>
<h1 id="1-개요">1. 개요</h1>
<p>안드로이드는 안드로이드 사에서 개발한 리눅스 기반의 운영체제입니다.
이 후, 구글이 안드로이드 사를 인수하여 스마트폰, 카메라 등의 장치에 맞게 설계하였습니다.
안드로이드 아키텍처는 다음과 같습니다.
최하위 레이어부터
<strong>리눅스 커널 -&gt; 라이브러리, 런타임 -&gt; 인터페이스 -&gt; 애플리케이션</strong></p>
<p>안드로이드의 필수 구성 요소는 총 4가지의 주요 기능으로 구성되어 있습니다.</p>
<h3 id="1-액티비티">1) 액티비티</h3>
<p>사용자에게 보여주는 디바이스의 인터페이스입니다. 화면은 메뉴를 클릭하거나 버튼을 클릭하는 등과 같은 특정한 액션에 의해 전환되는데, 각 화면이 모두 액티비티라고 할 수 있습니다.</p>
<h3 id="2-서비스">2) 서비스</h3>
<p>사용자들에게 보여주지 않고 백그라운드에서 액티비티가 실행되는 것과 비슷한 프로세스로 동작합니다. 액티비티가 화면에서 동작하는 동안, 이 기능이 함께 동작하는 경우가 많습니다. 예를 들어, 음악을 틀고 바탕화면으로 가도 음악이 재생되는 것이 있습니다. 웹으로 따지자면 액티비티는 프론트엔드 서비스는 백엔드 같다는 생각이 드네요.</p>
<h3 id="3-컨텐츠-프로바이더">3) 컨텐츠 프로바이더</h3>
<p>각 애플리케이션 사이에서 데이터를 공유하기 위한 인터페이스입니다. 안드로이드는 기본적으로 각 애플리케이션마다 샌드박스에서 동작하므로 각각의 애플리케이션 간의 데이터 접근은 격리됩니다. 그러므로, 컨텐츠 프로바이더 기능이 필요합니다.</p>
<h3 id="4-브로드캐스트-리시버">4) 브로드캐스트 리시버</h3>
<p>실시간으로 시스템의 상태 (배터리 잔량, 메일 알람 등)를 확인하여 이벤트가 발생했을 때 응답합니다. 또한, 노티피케이션 등을 이용해 사용자에게 알람을 발생합니다.</p>
<h1 id="2-실습에-활용할-앱과-취약점-목록">2. 실습에 활용할 앱과 취약점 목록</h1>
<p>책에서 인시큐어뱅크 버전 2 를 사용했습니다.
모바일 뱅킹 취약점 진단을 위한 테스트 용도로 제작되었으며 백엔드 서버는 파이썬입니다.
총 23가지의 취약점이 존재하며 이 정도의 취약점만 숙지하여도 상용 안드로이드 모바일 앱 취약점 진단은 충분하다고 합니다.</p>
<h3 id="2-1-취약점-목록">2-1) 취약점 목록</h3>
<blockquote>
<p><strong>1. 취약한 브로드캐스트 리시버
2. 취약한 인증 메커니즘
3. 로컬 암호화 취약점
4. 취약한 액티비티 컴포넌트
5. 루트 노출 및 우회 취약점
6. 취약한 컨텐츠 프로바이더
7. 취약한 웹 구현
8. 취약한 암호화 구현
9. 애플리케이션 패칭 취약점
10. 중요 정보 메모리 노출 취약점
11. 취약한 로깅 메커니즘
12. 페이스트보드 취약점
13. 애플리케이션 디버깅 취약점
14. 안드로이드 키보드 캐시 취약점
15. 안드로이드 백업 취약점
16. 런타임 조작
17. 취약한 SD 카드 스토리지
18. 취약한 HTTP 전송
19. 파라미터 조작
20. 하드코드된 중요 정보
21. 사용자 계정 목록화
22. 개발 백도어 취약점
23. 취약한 비밀번호 변경 로직</strong></p>
</blockquote>
<h1 id="3-자바-설치-및-환경-구성">3. 자바 설치 및 환경 구성</h1>
<h3 id="3-1-jdk-설치">3-1) JDK 설치</h3>
<p>안드로이드 스튜디오를 사용하기 위해 자바 소프트웨어 개발 도구를 설치합니다.
<a href="https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html">https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html</a>
<img src="https://velog.velcdn.com/images/sot_sky/post/bf58b8b1-3e0f-4f1e-9fe5-e00208ecee1e/image.png" alt="">
설치를 마치고 나면 윈도우가 자바 위치를 인지하도록 환경 변수를 등록합니다.
내 PC에서 우클릭 -&gt; 속성 -&gt; 고급 시스템 설정 -&gt; 환경 변수 -&gt; 시스템 변수 새로 만들기
변수 이름은 JAVA_HOME 경로는 설치한 자바 개발 도구 위치
<img src="https://velog.velcdn.com/images/sot_sky/post/95dc0419-6662-4b71-b87d-2f08bb5761ee/image.png" alt="">
확인 후 PATH 시스템 변수에서 자바 경로를 추가해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2b06cdff-f888-418a-93af-8dfc68e73a22/image.png" alt="">
%JAVA_HOME%\bin; 추가
그 후에 CMD창에서 java -version 입력
<img src="https://velog.velcdn.com/images/sot_sky/post/2204e429-1522-4614-911a-b9f332b8b386/image.png" alt="">
자바 개발 도구의 버전 정보가 출력 시 성공</p>
<h3 id="3-2-안드로이드-스튜디오-설치">3-2) 안드로이드 스튜디오 설치</h3>
<p><a href="https://developer.android.com/studio">https://developer.android.com/studio</a> 이 곳에서 다운로드하고 설치를 해줍니다.
설치 완료 후 SDK 매니저를 들어오면
<img src="https://velog.velcdn.com/images/sot_sky/post/80843a19-9114-4b4f-aad4-f286c84b900f/image.png" alt="">
여러 버전의 SDK를 확인할 수 있습니다. 
우선 실습할 안드로이드의 AVD를 설치하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/a4e172ad-ff15-435e-9190-1c58ac01a5c5/image.png" alt="">
넥서스 계열로 설치하겠습니다.
다음은 시스템 이미지 선택입니다. 안드로이드 시스템에서 사용될 버전을 선택하는 것으로 SDK 매니저로 API를 설치하면 시스템 이미지 항목이 목록에 추가됩니다. API가 최신이라고 무조건 좋은 것은 아니고 앱 환경에 따라 API 레벨을 선택합니다. 인시큐어 뱅크 앱은 API Level 24정도면 충분합니다. 이름 앞에 다운로드 링크를 클릭하면 다운로드가 됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/3debe633-47a9-47de-88b9-e43123a79c20/image.png" alt="">
다음은 가상 기기의 이름이나 저장 공간 등을 설정할 수 있습니다.
다음으로 ADB 환경 변수를 설정하겠습니다. ADB는 안드로이드 디버깅 도구입니다.
마찬가지로 내 PC -&gt; 속성 -&gt; 고급 시스템 설정 -&gt; 환경 변수에서 사용자 변수 PATH에 마지막 부분에 C:\Users\사용자 이름\AppData\Local\Android\Sdk\platform-tools를 추가해줍니다.
CMD에서 adb version을 입력해봅니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/29192246-10f7-4fe7-a1d5-f0efe9174e40/image.png" alt=""></p>
<h3 id="3-3-녹스-환경-설치">3-3) 녹스 환경 설치</h3>
<p>녹스를 설치하는게 더욱 간단하고 안드로이드 스튜디오와 비교했을 때 실행 시간도 훨신 빠를 것입니다.
녹스를 설치하면 처음에 태블릿 크기로 되어있는데 스마트폰으로 설정해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d810d760-ee9c-4d4f-84bf-9362da26bd2d/image.png" alt="">
또한 ROOT 권한을 조절하여 취약점 진단 시 활성화시킬 수 있습니다.
마찬가지로 NOX도 환경 변수 설정을 해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/28bd4128-d1a9-4bb0-969f-e882c4345de6/image.png" alt="">
이 후, CMD에서 nox_adb shell 입력 시 실행한 녹스 에뮬레이터 쉘에 접속이 됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d78d2600-4f4e-407d-a17a-e1cccb7b0796/image.png" alt="">
이제 녹스에서 앱을 실행하고 싶으면 nox_adb 안드로이드 스튜디오에서 실행하고 싶으면 adb를 입력하면 됩니다.
안드로이드 스튜디오에서 개발한 앱을 녹스 에뮬레이터에서 설치해 동작하도록 하려면 (더 빠르므로) 에뮬레이터에서 디버깅 접근을 허용해야 합니다.
녹스 에뮬레이터에서 설정 -&gt; 태블릿 상태 -&gt; 빌드 번호를 여러 번 누르다 보면 개발자 모드가 켜집니다. 그러면 개발자 옵션이라는 메뉴가 생기는데 거기서 디버깅을 허용해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/79541c7f-36df-4515-a420-7a96c2658e18/image.png" alt=""></p>
<h1 id="4-테스트">4. 테스트</h1>
<p>안드로이드 스튜디오에서 프로젝트를 하나 만든 후 테스트 앱을 만들어보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/fe42823a-4178-461e-8649-6d7614bf606c/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/1ccee78f-c4d6-433f-a372-b188f8040d93/image.png" alt="">
안드로이드 스튜디오에서 가상 기기에서 녹스 기기가 성공적으로 잡혔습니다.
이제 실행되는 앱을 대상으로 안드로이드 스튜디오에서 제공하는 로그캣 정보와 메모리 검색 정보 제공 기능을 사용할 수 있습니다.</p>
<h1 id="5-insecurebankv2">5. InsecureBankv2</h1>
<p><a href="https://github.com/dineshshetty/Android-InsecureBankv2">https://github.com/dineshshetty/Android-InsecureBankv2</a>
링크를 들어가면 인시큐어 뱅크 어플의 오픈 소스 코드가 나와 있습니다.
AndroLabServer은 백엔드 서버
InsecureBankv2는 인시큐어 뱅크 안드로이드 애플리케이션 소스
InsecureBankv2.apk는 인시큐어 뱅크 안드로이드 애플리케이션
Usage Guide.pdf는 사용자 가이드 입니다.</p>
<h3 id="5-1-녹스에-인시큐어뱅크-설치">5-1) 녹스에 인시큐어뱅크 설치</h3>
<p>cmd 창에서 <code>adb connect 127.0.0.1:62001</code> 입력하여 녹스의 기기와 adb와 연결시킵니다.
<code>adb devices</code>로 연결된 기기를 확인합니다.
그리고 adb install [인시큐어뱅크 apk경로] 를 입력하면 녹스 기기에 설치가 됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/b1137881-39df-4a76-b2be-125d27862e10/image.png" alt=""></p>
<h3 id="5-2-백엔드-서버-구축">5-2) 백엔드 서버 구축</h3>
<p>백엔드 언어는 파이썬 기반의 플라스크언어입니다.
파이썬 설칭 경로에 Scripts 폴더로 이동 후</p>
<blockquote>
<p>pip install flask sqlalchemy simplejson cherrypy web.py</p>
</blockquote>
<p>입력해서 필요한 라이브러리를 다운받습니다.
그 후, 명령창에서 앞 서 깃에서 다운받은 파일에서 AndroLabServer에서 app.py를 파이썬으로 실행해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/bff15e3e-e77d-4ded-8f98-80e0a445f9fd/image.png" alt="">
자꾸 오류가 나서 찾아보니 python3부터는 print가 함수가 돼서 괄호를 써주어야 하는데 현재 이 백엔드 서버는 python2로 작성돼서 이러네요. 그래서 호환성을 위해 파이썬2 버전으로 다시 설치했습니다.
AndroLabServer 디렉터리에 requirements.txt 파일로 모듈설치를합니다.</p>
<blockquote>
<p>python2 -m pip install -r requirements.txt</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/682897ce-2a85-499f-8303-98f75c3272fb/image.png" alt="">
pip 버전이 낮아 설치가 안되면 pip 업그레이드를 해줍니다.</p>
<blockquote>
<p>python2 -m pip install --upgrade pip
python2 -m pip install -r requirements.txt</p>
</blockquote>
<p>기존에 파이썬 3 버전과 구분하기 위해 python2로 exe파일 이름을 바꿨습니다.
이제 서버를 실행시키겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/3ae47133-b188-44cb-a9a2-4ef12dbb9506/image.png" alt="">
정상적으로 실행된 결과입니다.</p>
<h3 id="5-3-앱-설정">5-3) 앱 설정</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/da15ed5d-9e6d-45e9-8b1a-e7c9e5bb1d94/image.png" alt="">
preferences 메뉴에서 ip를 백엔드 서버 주소로 설정해주어야합니다.
submit 누른 후 로그인을 해줍니다.
디폴트 계정 정보는 다음과 같습니다.</p>
<p>dinesh/Dinesh@123$</p>
<p>jack/Jack@123$</p>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/a7c74246-fd9c-4f36-a77b-56663be0bbce/image.png" alt="">
성공적으로 로그인됐습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/b6072fe2-286d-4b62-bb2b-68939a506325/image.png" alt="">
서버를 실행한 cmd창에도 로그인 로그가 남습니다.</p>
<p>여기까지 안드로이드 앱 취약점 진단을 위한 환경 구축을 해보았습니다.
다음에는 컴파일방법에 대해 공부하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-aj1b3ofo</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-aj1b3ofo</guid>
            <pubDate>Tue, 06 Jun 2023 14:10:40 GMT</pubDate>
            <description><![CDATA[<p>오늘은 지뢰찾기 프로그램을 분석해보겠습니다.</p>
<h1 id="1-환경-구축">1. 환경 구축</h1>
<p>우선 윈도우 7 32bit 환경을 워크스테이션으로 구축했습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/44bf74a6-4970-41e6-9751-1cc567864b9f/image.png" alt="">
그리고 올리디버거 32bit로 다운받았습니다.
지뢰 찾기 게임은 <strong>ASLR 기술</strong>이 적용되어 있습니다.
ASLR이란 프로그램의 분석이나 버퍼오버플로우 등을 방지하기 위해 주소 값을 프로그램 실행 시마다 난수 값으로 랜덤화하여 할당하는 기술입니다.
다른 기술로는 스택 가드, 스택 쉴드가 있습니다.</p>
<h1 id="2-디버깅-시작">2. 디버깅 시작</h1>
<h3 id="2-1-시간-설정-변경">2-1) 시간 설정 변경</h3>
<p>일단 가장 쉬운 시간을 변경해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/05c1c1c4-a178-4ae4-805b-ba96a96998e3/image.png" alt="">
하단에 왼쪽은 시간, 오른쪽은 지뢰의 갯수를 나타냅니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/dc842dec-9f77-47e6-bdd5-677fcda06013/image.png" alt="">
올리디버거에서 상단의 File -&gt; Attach를 누르면 다음과 같이 실행 중인 분석 가능한 파일들이 나타납니다. 지뢰 찾기를 Attach 하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/89df147a-0156-4a05-832e-88834178e637/image.png" alt="">
윈도우 7에서 올리디버거를 사용하니 신기하고 새롭네요 ㅋㅋ..
가장 만만한 시간을 건드려보기 위해 해당 함수에 브레이크 포인트를 설정해야합니다.
시간에 관련된 함수로 SetTimer()함수가 있습니다. 이 함수로 가정하고 내부 모듈 호출을 모두 찾아보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/1f8a78dc-0f43-48b2-95eb-bd2305c90bb8/image.png" alt="">
SetTimer()함수에 대한 정의
여기서 uElapse는 밀리세컨즈의 단위로 1000이면 1초에 한 번씩 타이머가 동작한다는 의미입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/03acfc4b-bf1f-4c39-a6d0-217b4f9a9565/image.png" alt="">
SetTimer()함수 호출이 있네요.
이 곳에 브레이크 포인트를 설정해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/90ab7d0e-bcc0-4486-9f6c-969aa2bdbcf2/image.png" alt="">
예상대로 프로그램을 재시작하자 타이머가 올라가지 않습니다.
그럼 이제 코드를 보며 SetTimer()함수에 어떤 인수가 들어가는지 확인해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/63e264f5-5a31-49fa-bebd-5467acd0d873/image.png" alt="">
여기서 Timeout를 가리키는 PUSH 3E8은 PUSH 1000을 의미합니다. -&gt; 3E8(16) = 1000(10)
그럼 저 인수인 3E8값을 5배로 늘린다면 시간은 5초에 1초씩 타이머가 동작하여 시간이 5배 느리게 흘러갈 것입니다.
타이머를 조작하기 위해 5000을 헥사 값으로 나타내면 1388입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/186e9f74-be0b-4239-a46b-23484afa9fcb/image.png" alt="">
다음과 같이 수정을 하고 별도의 파일로 저장하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/0b72c8e2-1f6b-4331-b045-6638264c0d69/image.png" alt="">
프로그램 실행 시 참조 DLL파일이 위치한 원본 실행 파일이 있는 곳에 이름만 바꾼 후 저장합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/62b7ed69-a6ae-4474-9d36-3aa3771f332b/image.png" alt="">
5초에 1초씩 타이머가 동작합니다.</p>
<h1 id="3-맵핵-만들기">3. 맵핵 만들기</h1>
<p>지뢰 찾기에서는 지뢰의 위치를 알아내는 것이 맵핵입니다.
가장 먼저 프로그램을 리버싱하여 지뢰의 위치를 알아내고 그것을 화면에 구현해줄 프로그램을 만들어야 합니다.
일단 가정을 해보겠습니다.
게임을 진행하며 3가지의 함수 호출을 예상할 수 있습니다.</p>
<pre><code>1. 타일을 누름과 동시에 타이머가 동작될 때 SetTimer() 함수 호출
2. 지뢰를 눌러 게임이 종료 되었을 때 게임을 다시 할지 물어보는 창을 호출하는 GetDlgItem() 함수 호출
3. 타일을 눌렀을 때 호출되는 함수를 사용하는 것으로 GetKeyState() 함수 호출</code></pre><p>이 세 가지 중에 하나를 정하여 맵핵을 만들 수 있습니다. 그러나 어떤 방법이 가장 좋은지 정답은 없습니다. 리버싱의 오랜 경험과 직관적인 능력을 활용해야 합니다.</p>
<p>세 번째 방법으로 시도를 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/a0289d47-e5ce-47a8-8509-700a93f89b7d/image.png" alt="">
GetKeyState() 함수는 가상 키 값을 인자로 받아 가상 키의 상태를 숫자로 반환하는 함수입니다. 지뢰 찾기 게임의 경우 타일들이 가상 키로 인자가 되고 상태를 함수를 통해 체크합니다.
우클릭 -&gt; search for -&gt; search name in current module 로 이동을 하면 현재 모듈에서 사용하는 모든 함수와 문자열을 보여줍니다.
여기서 GetKeyState() 함수를 찾아보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/c3ba7cd9-9a1e-4617-aced-036a64fb8326/image.png" alt="">
그리고 names 메뉴에서 해당 함수를 우클릭 -&gt; Find References를 누르면 함수가 어디서 호출되는지 목록이 나옵니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/70ee171e-5346-4463-a712-8f817e9462f5/image.png" alt="">
명령어 형식을 보면 CALL로 시작하는 함수 직접 호출이 있고, MOV로 명령어의 주소를 EDI에 저장해 놓고 나중에 CALL EDI와 같은 방식으로 함수를 호출합니다.
search for -&gt; all intermodular calls 로는 직접 호출만 볼 수 있지만,
search for -&gt; names 로는 간접적인 호출도 모두 볼 수 있어 함수가 사용되는 모든 영역을 확인할 수 있습니다.
이제 정확히 어디서 함수가 호출되는지 알아보기 위해 모두 브레이크 포인트를 설정해주고 프로그램을 다시 시작해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8758c759-c537-4bca-a3c6-7c1bfd9e4c01/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/ba59437f-9185-44c9-9931-8f59932ca519/image.png" alt="">
프로그램이 실행되기 전에 한번 걸립니다.
GetKeyState() 함수는 가상 키가 눌려지는 것을 지속적으로 모니터링하고 있기 때문에 프로그램 로딩 시 호출될 수 있다. 
타일이 눌려질 때 호출되는 부분을 찾기 위해 브레이크 포인트를 해제하고 건너 뛰겠습니다.
다시 F9을 눌러 실행하자 프로그램이 실행되었습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/dc6350f7-bd59-4b7b-adac-0202afa3f148/image.png" alt="">
타일을 하나 눌러보자 
<img src="https://velog.velcdn.com/images/sot_sky/post/a80702bc-0598-4973-82f5-2864c9f83e4e/image.png" alt="">
008FC7FA에서 BP가 걸립니다. 다시 F9으로 재게 시 008FC826에서도 BP가 걸립니다.
다시 F9으로 재게 시 타일은 뒤집히지 않았습니다. GetKeyState() 함수가 가상 키가 눌려지는 이벤트를 지속적으로 모니터링하고 있어 다음 상태로 넘어가지 않는 것입니다. 따라서 이 두 BP를 해제하겠습니다.
이 후 다시 타일을 클릭하자 008FC950에서 BP가 잡혔습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/146c1e17-49fd-4b0d-a7e5-cd8d7fe1a6da/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/39a3fcae-3d1f-43ba-a80f-77fe2f33d274/image.png" alt="">
게임을 보니 제가 클릭한 타일이 눌려져 있었습니다. 그럼 아마 제가 누른 부분을 지뢰인지 아닌지 비교하는 비교문이 따라올 것이고 그 정보가 맵핵을 만들 실마리가 될 수 있습니다.
그러므로, 008FC950을 제외한 모든 BP를 제거하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/bbc4ce96-7dd9-4a89-80b0-47dba9908839/image.png" alt="">
이제 부터 BP아래에 오는 비교문들을 분석해보겠습니다.</p>
<blockquote>
<h3 id="1-첫번째-비교문">1. 첫번째 비교문</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/7c64ee53-faa2-4c18-926d-8bf412f6db9b/image.png" alt="">일단 가장 먼저 오는 TEST AL, AL을 보시면 EAX의 하위 1바이트가 NULL인지 확인하는 함수입니다. 만약 NULL인 경우 제로 플래그가 1이 설정됩니다.
현재의 경우 제로 플래그가 1이되어 JE의 주소로 점프를 할 것입니다.
분기문의 역할을 알 수 있는 가장 좋은 방법은 제로 플래그를 바꿔 무슨 변화가 일어나는지 확인하는 방법입니다. 제로 플래그를 더블 클릭해서 점프를 시키지 않고 실행해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/4fce7c46-0194-4a96-a7b3-1faebd4eb374/image.png" alt="">
제가 눌렀던 타일이 다시 원상복귀 되었습니다. 이렇게 분기문의 역할을 알아볼 수 있습니다.</p>
</blockquote>
<h3 id="2-두번째-비교문">2. 두번째 비교문</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/62b0afd0-b827-4246-b8ab-9ce7cbe0582a/image.png" alt="">
JNZ에서 제로 플래그가 1로 점프하지 않지만 제로 플래그 값을 변화시켜 점프를 하고 실행하자
<img src="https://velog.velcdn.com/images/sot_sky/post/34ce95b0-a93c-42f1-8364-b3a4ebebfe46/image.png" alt="">
제가 클릭한 타일에 깃발이 생겼습니다. 즉, 사용자가 마우스 오른쪽 클릭으로 지뢰를 표시했는지 체크하는 부분입니다.
이 비교문도 맵핵을 위해서는 그다지 필요한 정보는 아닌것 같습니다.</p>
<h3 id="3-세번째-비교문">3. 세번째 비교문</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/d119f7af-53af-4c95-be0e-e8f6a054fd65/image.png" alt="">
마찬가지로 점프 부분에서 제로 플래그를 변경해보았습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/acf76a8b-7c9f-4e9d-87b0-4be10eb6d797/image.png" alt="">
그런데 딱히 별 다른 동작은 없고 클릭한 타일의 주위에 지뢰가 없는 타일을 모두 뒤집습니다.</p>
<p>이제 다음으로는 스텝 인투를 하여 호출되는 함수 안으로 들어가 보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/42c2dfcf-00f1-4e08-ae03-11616728a0bc/image.png" alt="">
조금 내려오다 보면 CALL 명령이 있습니다. 여기서 스텝 인투 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d3cd5863-cffc-4806-8fdd-499b26fc3191/image.png" alt="">
점프 후 전형적인 스택 프레임 생성과정인 명령이 나옵니다.</p>
<pre><code>PUSH EBP
MOV EBP, ESP</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/e12d2da0-d1a6-44e3-bba8-aba7cea1fe7a/image.png" alt="">
스탭 오버로 진행 중 또 다른 CALL을 만나 일단 BP를 설정하고 해당 CALL로 스텝 인투 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/6d0f4f70-6d51-4960-ab51-61b06b954b87/image.png" alt="">
리턴이 아닌 점프로 함수를 끝내고 있습니다. 그러므로 서브루틴을 종료하는 것이 아니고 나중에 서브루틴이 종료되면 00BA6FE3 주소로 돌아갑니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/dbfaa9b5-26e8-4651-871d-f61347edd559/image.png" alt="">
마지막 점프 전에 스택 상태는 다음과 같았습니다.
중간에 인자 1, 1을 두개를 넣어주었습니다. 이 인자가 무엇을 의미하는지는 아직 정확히 모르지만 다음 점프할 주소의 서브루틴(00BA0C50)에서 명확해질것 같습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ce7aeadb-4d3a-477c-8008-c0bd7ced9af0/image.png" alt="">
이제 세번째 서브루틴인 00BA0C50으로 점프를 했습니다.
여기까지 왔으므로 BP를 설정하고 분석을 하겠습니다.
우선 새로운 스택 프레임 생성까지(00BA0C53) 실행을 했습니다.
스택 프레임 구조에서는 스택에 있는 값을 참고할 때 EBP를 기준으로 사용하므로 서브루틴을 분석할 때는 EBP 레지스터 값을 설정한 후에 분석을 시작하는 것이 좋습니다.
00BA0C57 주소에 있는</p>
<pre><code>MOV EBX, DWORD PTR SS:[EBP+8]</code></pre><p>명령은 X좌표를 설정하는 명령어입니다.
EBP 레지스터로부터 8바이트 떨어진 부분에서 4바이트를 읽어 EBX레지스터에 넣습니다.
00BA0C6A 주소에 있는</p>
<pre><code>MOV EDI, DWORD PTR SS:[EBP+C]</code></pre><p>명령은 Y좌표를 설정하는 명령어 입니다.
이전 서브루틴에서 인자인 1두개는 X좌표와 Y좌표를 뜻합니다.
다시 확인을 해보기 위해 다시 실행해 다른 곳을 눌러보았습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/e4ac6e92-8819-486e-8b47-f1dd8865ec7f/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/e17d9ca5-a800-428a-8e46-c13c23c91e3d/image.png" alt="">
X좌표 4와 Y좌표 1 인자가 들어가는 것을 볼 수 있습니다.
이어서 분석하는 중 새로운 비교문을 만났습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/e05430c6-a3bd-4fc1-9903-16618adcd321/image.png" alt="">
제로 플래그를 변경하고 실행하자
<img src="https://velog.velcdn.com/images/sot_sky/post/a5483b30-ee9a-435e-b816-c172ba18fbe1/image.png" alt="">
지뢰가 나오더니 패배 메세지가 떴습니다.
즉, 해석을 해보면 CMP EAX, 9 에서 만약 EAX가 9가 아니라면 제로플래그는 0이고 분기에서 점프를 하지 때문에 지뢰를 클릭한 동작으로 진행됩니다.</p>
<p>다음 비교문으로 넘어가겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/c83cf701-bb9a-4d6f-90da-b10cc38c5205/image.png" alt="">
해당 명령어의 메모리 덤프를 확인하며 여러번 실행을 해보니 첫 메모리 숫자가 제가 클릭한 타일의 갯수만큼 늘어나는 것을 확인했습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/acbd1929-282d-4b14-8d2d-7f48facb6e79/image.png" alt="">
처음 클릭 후 1 증가된 모습
<img src="https://velog.velcdn.com/images/sot_sky/post/59802d11-488f-45e5-8a15-e24620c0f5f8/image.png" alt="">
즉, 두번째 클릭부터는 00BA0C95의 비교문에서 결과가 달라져 제로 플래그가 0이므로 JNZ 분기문에 의해 지정 주소로 점프하게 됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/896a57b4-dcce-4dff-8e66-b3fcc6c6234e/image.png" alt="">
점프 후 여러번 실행을 해보며 00BA0CC6 주소에서 비교하는 CMP BYTE PTR DS:[EDI+EAX], CL에서 제로 플래그가 1인 경우 지뢰가 아니고 0인 경우 지뢰인 로직을 알아 냈습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/1429567b-6bb2-4188-a99f-08ff6c9a79f2/image.png" alt="">
코드를 보면 X좌표를 이용해 뭔가를 계산합니다. 이어서 EDI(Y좌표)를 이용해 EAX와 더한 메모리 위치의 값을 CL과 비교하여 제로 플래그가 0인 경우 클릭한 타일의 좌표가 최종적으로 지뢰인지 아닌지를 알 수 있습니다.
이제 x좌표와 y좌표만 있으면 지뢰인지 아닌지를 찾을 수 있습니다.</p>
<h1 id="4-코드-인젝션">4. 코드 인젝션</h1>
<p>일단 코드를 삽입할 빈 공간을 찾아야 하므로 프로그램 밑 부분으로 이동했습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ca2247ea-6601-4f66-aefb-097f9fa708a6/image.png" alt="">
코드 인젝션을 하기 위해 
<img src="https://velog.velcdn.com/images/sot_sky/post/6ad34a28-97f1-4b51-b36e-0fb5ebb13d4a/image.png" alt="">
임의의 빈 공간으로 점프를 시켰습니다.
이제 0010DF75에 코드를 작성합니다.</p>
<pre><code>CMP DWORD PTR DS:[ESI+18],ECX 
JE 000B0C9A
------------------------------&gt; 원본 코드에서 점프해온 곳으로 원본 코드의 대체 명령

PUSH EAX
PUSH EDX
PUSH ECX
PUSH EBX
PUSH EDI
------------------------------&gt; 맵핵 프로그램에서 사용하는 레지스터의 값을 스택에 넣어 놓음으로서 맵핵 기능이
끝나면 다시 꺼내 다음 명령어에서 문제없이 사용하기 위해 백업

MOV EDX, 0
MOV ECX, 0
MOV EDI, 0
MOV EBX, 0
------------------------------&gt; 사용할 레지스터를 초기화

MOV EAX, DWORD PTR DS:[ESI+44]
MOV EAX, DWORD PTR DS:[EAX+0C]
MOV EAX, DWORD PTR DS:[EBX*4+EAX]
MOV EAX, DWORD PTR DS:[EAX+0C]
MOV CL, BYTE PTR DS:[EAX+EDI]
------------------------------&gt; EBX, EDI에 0부터 80까지 차례대로 값을 넣어 81번 반복하면서 지뢰가 저장돼있는지 
확인

MOV BYTE PTR DS:[EDX+4B5850], CL
------------------------------&gt; 지뢰 확인 결과 빈 메모리에 저장 

INC EBX
------------------------------&gt; X좌표인 EBX 값을 0부터 1씩 증가

INC EDX
------------------------------&gt; 지뢰 확인 결과를 특정 메모리에 저장시키는데 메모리 위치 0부터 80까지 차례대로 
저장

CMP EBX, 9
JL SHORT 0010DF97
------------------------------&gt; EBX가 9보다 작을 경우 0010DF97 주소로 점프 반복문

INC EDI
------------------------------&gt; Y좌표인 EDI 값을 0부터 1씩 증가

CMP EDI, 9
JNE SHORT 0010DF92
------------------------------&gt; EDI가 9보다 작을 경우 0010DF92 주소로 점프 반복문

POP EDI
POP EBX
POP ECX
POP EDX
POP EAX
------------------------------&gt; 레지스터 값 복구

JMP 00B00CBA
------------------------------&gt; 원래 로직으로 복귀</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/b6aa3574-5998-4370-a5e4-48f33c7f6378/image.png" alt=""></p>
<p>프로그램 실행하자 지정한 메모리 주소에 지뢰의 위치가 01로 표기됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/008d1fbc-b968-42e4-bd25-331ba485b92a/image.png" alt="">
4B5850부터 81개 까지 표기됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f7bc082f-aa8b-4864-9739-cb3ec926e1e3/image.png" alt="">
메모장에 표기해봤습니다. 그럼 01위치를 한번 눌러보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7c24ccec-c550-46e4-9511-4949d1602f5f/image.png" alt="">
지뢰 위치가 정확히 일치하네요.</p>
<h1 id="5-마무리">5. 마무리</h1>
<p>책 내용을 이해하는데 정말 오래걸렸다..
이해가 너무 안돼서 따라하면서 설명 부분을 적어도 20번씩 읽었던 것 같다..
이해하려고 노력하다가 중간부턴 이해한다기보단 그냥 외워버린다는 마인드로 임한것 같다.
이게 기초적인 실용 프로그램 해킹이라니 정말 놀랍다 ㅋㅋㅋ
그러면 악성코드나 요즘 사용되는 상용 프로그램들은 어느정도일까..
어셈블러 프로그래밍도 이해해보면서 여러가지를 배웠다.
예를 들어, 사용할 레지스터를 먼저 백업시키고 초기화한다던가 반복문을 쓰는 형식에 대해서도 어느정도 윤곽이 잡혔다. 또한, 리버싱을 할 땐 어느 부분이 나에게 필요한 부분인지 아는 능력이 정말 중요한것 같다. 이러한 경험들을 해보며 배웠던 스택 프레임, 메모리, 레지스터, 주소 등 기초지식들이 점점 머리에 스며드는 것 같다. 아직 많이 부족하지만 열심히 배우며 성장하고 싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-401cusbm</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-401cusbm</guid>
            <pubDate>Sun, 04 Jun 2023 10:48:21 GMT</pubDate>
            <description><![CDATA[<p>저번 시간에 MUP과정에서 OEP를 찾고 언패킹을 해보았습니다. 
OEP를 찾는 것은 정해진 답이 없어 다양한 경험과 지식을 필요로 합니다.
이번 시간에는 여러가지 사례로 OEP를 찾는 것을 해보겠습니다.</p>
<h1 id="1-esp-레지스터를-이용한-oep-찾기">1. ESP 레지스터를 이용한 OEP 찾기</h1>
<p>레나 20강의 프로그램 실행하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/c34804b0-e97c-40d5-b402-2e4fc3d59d3f/image.png" alt="">
EZIP으로 패킹된 프로그램으로 간단하게 리버싱은 불가합니다.</p>
<p><strong>스택 프레임</strong>은 함수 호출 시 생성되는 공간으로 안에 파라미터와 지역 변수를 저장합니다.
함수가 실행 중일 때 존재하고 종료되면 사라집니다. 또한, 기준점을 중심으로 데이터를 참조하는데 그 기준점은 <strong>EBP 레지스터</strong> 값을 말합니다. -&gt; EBP 레지스터를 <strong>프레임 포인터</strong>라고 하고 ESP 레지스터를 스택 포인터라고 합니다.
앞에서 서브루틴 호출 과정을 스택 프레임의 관점에서 보았지만 이번엔 언패킹의 관점에서 바라보겠습니다.</p>
<blockquote>
<p>서브루틴 호출</p>
</blockquote>
<ol>
<li>PUSH EBP -&gt; 기존의 프레임 포인터 값 백업</li>
<li>MOV EBP, ESP -&gt; 호출할 서브루틴의 스택 프레임 생성 (즉, 호출 서브루틴의 새로운 EBP 생성)</li>
</ol>
<blockquote>
<p>서브루틴 종료</p>
</blockquote>
<ol>
<li>MOV ESP, EBP -&gt; ESP를 EBP를 백업해 놓은 스택 주소로 이동</li>
<li>POP EBP -&gt; EBP 값 복구</li>
</ol>
<p>만약 호출하는 서브루틴이 언패킹하는 로직일 경우 
서브루틴에 들어가기 전 ESP 레지스터에 저장된 주소가 가리키는 값은 현재 루틴의 EBP 레지스터의 값일 것입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/18bbb5ea-a7e8-4df9-a19c-2209c7c0e9fe/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/7b0a4e75-ac93-4401-a452-9e9326f1657c/image.png" alt="">
<strong>왜냐하면, PUSH EBP를 하므로 현재 ESP의 주소에는 EBP의 값이 들어있기 때문입니다.</strong></p>
<p>또한, 언패킹 서브루틴의 로직이 종료되고 이전 함수로 복귀 시 ESP는 다시 EBP 레지스터 값을 읽기 때문에 값을 읽는 부분에 브레이크포인트를 설정하여 언패킹 과정이 끝난 후 OEP로 점프하는 부분을 찾아 프로그램을 언패킹 할 수 있습니다.</p>
<h1 id="2-브레이크-포인트">2. 브레이크 포인트</h1>
<p>올리디버거는 <strong>소프트웨어 브레이크 포인트</strong>와 <strong>하드웨어 브레이크 포인트</strong>가 있습니다.</p>
<h3 id="2-1-소프트웨어-브레이크-포인트">2-1) 소프트웨어 브레이크 포인트</h3>
<p>일반적으로 F2키를 눌러 사용하는 소프트웨어 브레이크포인트는 개수에 제한 없이 필요한 만큼 만들어서 쓸 수 있습니다. 
브레이크 포인트 설정 시 해당 명령의 맨 앞 부분의 1바이트가 <strong>CC(INT 3)</strong>으로 대체되어 운영체제가 코드를 실행하면서 해당 코드를 만나면 인터럽트를 발생시킴으로써 실행에 대한 제어권을 인터럽트 핸들러에게 넘깁니다.
하지만, 올리디버거는 사실 맨 앞의 코드를 CC로 변경하는 것이 아니라 udd 파일에 브레이크 포인트 주소를 기록해놓고 디버거가 udd파일에 기록된 주소를 실행하면 디버깅을 잠시 멈추는 방법으로 사용합니다.</p>
<blockquote>
<p><strong>인터럽트와 API 후킹 기술</strong>
소프트웨어 인터럽트를 설정하면서 인터럽트가 호출될 때 수행될 함수를 등록합니다.
이것을 인터럽트 서브루틴이라고 부르고 <strong>콜백(Callback) 함수</strong>로 사용합니다.
<strong>즉, 특정 함수 호출 시 인터럽트를 설정하고 콜백 함수에 입력 값을 다른 값으로 바꾸는 로직을 심어 로직을 바꾸어 사용자의 의도와는 전혀 다른 결과를 발생시키는 기술을 API 후킹 기술이라고 합니다.</strong></p>
</blockquote>
<h3 id="2-2-하드웨어-브레이크-포인트">2-2) 하드웨어 브레이크 포인트</h3>
<p>하드웨어 브레이크 포인트는 명령어가 바뀌는 것이 아니라 <strong>프로세서에서 디버거를 위해 특별히 제공하는 레지스터인 디버거 레지스터에 브레이크 포인트를 설정하고자 하는 주소를 입력</strong>합니다.</p>
<p>32비트의 아키텍처의 경우 8개의 디버거 레지스터가 제공되고 이 중 DR0 ~ DR3까지 총 4개의 레지스터가 브레이크 포인트를 위해 사용됩니다.
하드웨어 브레이크 포인트의 장점은 명령어 뿐 아니라 <strong>메모리에도 브레이크 포인트 설정</strong>이 가능합니다.</p>
<p>메모리 우클릭 -&gt; breakpoint -&gt; hardware, on write -&gt; byte,word,dword</p>
<h1 id="3-oep-찾기와-언패킹된-프로그램-저장">3. OEP 찾기와 언패킹된 프로그램 저장</h1>
<p>이제 본격적으로 20강 프로그램의 OEP를 찾고 언패킹을 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/17f0f628-b859-483e-b80e-bb36545d6ef8/image.png" alt="">
우선 DIE로 파일을 보니 ezip으로 패킹되어 있습니다.
또한, 엔트로 포인트가 004650be로 설정되어 있네요.
<img src="https://velog.velcdn.com/images/sot_sky/post/f451198c-8928-491e-a122-c0cbd8123f6e/image.png" alt="">
올리디버거로 실행 시 004650be에서 실행이 멈춥니다.
F8을 눌러보니 바로 서브루틴 호출 과정이 보입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/e118912a-297f-4c40-bd15-44f5d1d6fbb4/image.png" alt="">
여기서 레지스터와 스택의 상태를 봐보자면
<img src="https://velog.velcdn.com/images/sot_sky/post/c14998e4-5aed-4ab6-b2db-1254430d8c16/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/ef8b7369-b11f-427e-acaf-1d94b89bc0ee/image.png" alt="">
ESP 레지스터 값의 스택 주소에 EBP 레지스터 값이 들어가 있는 것을 볼 수 있습니다.</p>
<p>즉, 서브루틴 종료 시에도 ESP 위치의 백업해 놓은 EBP 값을 POP 시켜야하므로 현재 ESP의 메모리 값을 참조할 것이다. 그래서 하드웨어 브레이크 포인트를 이용하여 메모리에 브레이크 포인트를 설정하면 OEP로 이동하는 지점을 확인할 수 있습니다.</p>
<blockquote>
<p><strong>프레임 포인터</strong>
서브루틴이 지역 변수를 할당하기 위해 사용하는 공간을 <strong>&#39;스택 프레임&#39;</strong> 이라고 합니다.
스택 프레임의 내부의 값들은 EBP 레지스터에 저장된 주소를 기준으로 상대 주소를 지정해서 사용합니다. 바로 EBP 레지스터에 저장된 주소를 <strong>&#39;프레임 포인터&#39;</strong> 라고 합니다.</p>
</blockquote>
<p>이제 메모리에 브레이크 포인트를 설정하기 위해 ESP 레지스터를 우클릭 후 Follow Dump로 메모리의 위치를 찾습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/4670bc4b-37bb-494e-b6d0-87172cce4ecd/image.png" alt="">
첫 부분을 우클릭 후 -&gt; Breakpoint -&gt; hardware, on access -&gt; dword를 해줍니다.
Access 동작에 의한 브레이크 설정을 하는 이유는 데이터를 읽는 동작에 대해 설정해야 하기 때문이고 dword는 4바이트의 단위로 데이터를 읽기 때문에 선택했습니다.</p>
<p>그 후, 위에 Debug 메뉴에서 Hardware Breakpoint를 누르면
<img src="https://velog.velcdn.com/images/sot_sky/post/3ec8159b-ec6e-49fe-a084-13fc6d5087a3/image.png" alt="">
다음과 같이 하드웨어 브레이크 포인트 목록이 나옵니다.</p>
<p>이제 F9로 프로그램을 실행하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7b510783-0ef3-4f0d-8582-5cf2c50a0561/image.png" alt="">
00468687 까지 실행을 하고 브레이크가 걸립니다.
즉, 이 부분이 기존 서브루틴의 EBP 값을 복구하기위해 아까 브레이크를 설정해 놓은 메모리를 참조하는 부분입니다. 
<img src="https://velog.velcdn.com/images/sot_sky/post/250d82eb-b9f0-4245-8350-cd4762eb0cf4/image.png" alt="">
그 다음, JMP EAX를 통해 OEP로 이동하는 것을 볼 수 있습니다.
F8로 스텝 오버 해보면
<img src="https://velog.velcdn.com/images/sot_sky/post/597415db-ccc7-41c4-97d6-e887880841f6/image.png" alt="">
주소 004271B0으로 점프 후 전형적인 스택 프레임을 생성하는 코드를 확인할 수 있습니다.
즉, OEP는 주소 004271B0임을 알 수 있고 이제 덤프를 하여 새로운 파일을 만들어보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f778a797-e954-4b5e-a5f9-78a4f1439022/image.png" alt="">
생성한 덤프파일 디버거 실행
<img src="https://velog.velcdn.com/images/sot_sky/post/30965b32-2767-4c51-9cc6-281ff7399313/image.png" alt=""></p>
<h1 id="4-코드-인젝션">4. 코드 인젝션</h1>
<h3 id="4-1-원리">4-1) 원리</h3>
<p>코드 인젝션 기술은 PE 파일의 빈 공간에 셸코드를 입력해서 실행하게 만드는 해킹 기술입니다.
앞서 배운 코드 케이브 기술을 활용하며, 기계어로 만들어진 셸코드를 이용해 공격자가 원하는 동작을 수행하기 위해 사용합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2ed5b6c6-1902-41a2-9f4a-d889ced80d1d/image.png" alt="">
우선 프로그램 내부에서 점프할 부분을 자신이 코드를 작성할 주소로 점프하는 코드로 변경합니다.
셸 코드가 보통 100바이트가 넘어가기 때문에 코드 케이브와 같이 빈 공간에 작성해줍니다.
이 때, 원래 Instruction C인 원래 명령을 가장 윗 부분에 작성해주고 그 밑에 임의의 셸코드를 입력한 후 그 밑에는 다시 Instruction D로 돌아오는 점프 코드를 작성해줍니다.</p>
<h3 id="4-2-실습">4-2) 실습</h3>
<p>디버거로 abexme1.exe파일을 열어보겠습니다.</p>
<blockquote>
<ol>
<li><strong>실행 가능한 메모리 영역에 집어넣어야 하므로 메모리 맵에서 확인해야합니다.</strong>
<img src="https://velog.velcdn.com/images/sot_sky/post/39d6260f-0035-4907-a56b-be968cd9d067/image.png" alt="">
&amp;nbsp</li>
<li><strong>셸코드 찾기</strong>
EXPLOIT DATABASE 홈페이지에서 찾을 수 있습니다.
<a href="https://www.exploit-db.com/exploits/33836">https://www.exploit-db.com/exploits/33836</a> 에 사용자를 강제로 추가하는 기능을 가진 셸코드가 있다고 합니다. 우선 확인해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/971c3ff3-5bac-4515-b4c0-fb3bb3bb514b/image.png" alt="">\x는 16진수의 표현을 의미하는데 메모리에 직접 16진수를 넣어야하므로 제거하겠습니다.
&amp;nbsp
31d2b230648b128b520c8b521c8b42088b72208b12807e0c3375f289c703783c8b577801c28b7a2001c731ed8b34af01c645813e57696e4575f28b7a2401c7668b2c6f8b7a1c01c78b7caffc01c7684b336e01682042726f682f414444686f727320687472617468696e6973682041646d68726f75706863616c676874206c6f6826206e656844442026686e202f4168726f4b3368336e20426842726f4b68736572206865742075682f63206e686578652068636d642e89e5fe4d5331c05055ffd7
가 되었습니다.
&amp;nbsp
&amp;nbsp</li>
<li><strong>셸코드 입력</strong>
이제 코드영역에 셸코드를 넣어보겠습니다. 대부분 밑으로 내려가다 보면 빈 공간이 많이 남아 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2375a4b3-0bae-49b9-89f4-3f9acb9a6713/image.png" alt="">이 곳에 셸코드를 작성할 것입니다.
우선, 가장 처음 명령을 00401067로 점프하는 명령으로 바꾸고 00401067에 가정 처음 명령인 PUSH 0을 똑같이 쓰겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/732163b9-4dd6-418f-b5e3-a400aec65367/image.png" alt="">이제 밑에 셸코드를 인젝션하겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/5afd5197-bab7-4aa2-9c80-183df455d942/image.png" alt="">
우클릭 후 -&gt; binary -&gt; edit -&gt; keep size를 해제하고 위의 코드를 붙여넣었습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/4f187e38-eabf-4698-88e4-7b878a5d0f94/image.png" alt="">
&amp;nbsp</li>
<li><strong>파일 저장</strong>
우클릭 후 -&gt; copy to excutable -&gt; all modification -&gt; 우클릭 -&gt; save file
abex1_hacked.exe 로 저장했습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/a29bc6a9-5fb0-4c9e-a463-5d7f837e6e6c/image.png" alt="">
&amp;nbsp</li>
<li>파일 실행
사용자 계정 추가는 관리자 권한이 필요하기 때문에 관리자 권한으로 실행 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/af38b3fa-aa58-4425-9321-1df294a319b3/image.png" alt="">
흠.. 아무래도 윈도우 11 버전까지 오며 사용자 계정을 추가하는 동작을 하는 셸코드가 달라진것 같습니다. </li>
</ol>
</blockquote>
<h1 id="5-마무리">5. 마무리</h1>
<p>언패킹 관점에서 스택 프레임 생성 과정을 보며 한층 더 깊은 이해를 하였고 API 후킹에 대해 알 수 있었다.
인터럽트 API 후킹이란 인터럽트 시 수행될 함수를 콜백 함수라고 부르는데 특정 함수 호출 시 임의로 인터럽트를 설정하여 콜백 함수로 들어갈 때 입력 값에 변화를 주는 로직을 심음으로써 사용자의 의도와는 전혀 다른 결과를 발생시키는 기술이다.
또한, 메모리에 브레이크 포인트를 설정할 수 있는 하드웨어 브레이크 포인트에 대해 배웠다.
그리고, 코드 인젝션에 대해 배웠다. 다소 API 후킹과 헷갈릴 수 있지만, <strong>코드 인젝션은 API 후킹과 달리 코드 케이브를 이용해 아예 다른 주소로 동작 순서를 옮겨 동작을 변화시키지만 API 후킹은 인터럽트 후 콜백 함수에 입력 값만을 변조하여 동작을 변화시키는 것이다.</strong> 
비록, 실습으로 원하는 결과를 얻진 못하였지만 기본적인 코드 인젝션 기술을 써보았다는 것에 만족한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-jklagwr6</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-jklagwr6</guid>
            <pubDate>Fri, 26 May 2023 03:43:00 GMT</pubDate>
            <description><![CDATA[<h1 id="1-패킹과-언패킹">1. 패킹과 언패킹</h1>
<p><code>패킹(Packing)</code>이란 프로그램 코드 크기를 줄이려고 압축하거나 <strong>프로그램 분석을 어렵게 만들려고</strong> 암호화하는 것을 말합니다.
<code>프로텍팅(Protecting)</code>은 목적이 <strong>코드를 난독화하여 분석을 어렵게 만드는 기술</strong>이며 프로그램 실행 속도 저하와 프로그램의 크기가 증가하는 단점이 있습니다.
<code>컴프레싱(Compressing)</code>은 단순 압축을 하는 방법으로 초기에 하드디스크 용량이 작아 사용된 기술입니다.</p>
<p>UPX로 패킹된 파일은 <strong>언패킹에 필요한 코드 영역과 패킹된 데이터가 들어가 있는 영역</strong>, 그리고 <strong>언패킹된 데이터가 저장된 빈 공간</strong>으로 구성됩니다.</p>
<ol>
<li>운영체제의 로더가 실행 파일을 메모리에 로딩합니다.</li>
<li>진입점(Entry point)으로부터 프로그램이 실행됩니다. 엔트리 포인트는 언패킹 코드 영역에 들어 있습니다.</li>
<li>언패킹 코드는 패킹된 데이터를 하나씩 읽어 압축을 풀고 공간에 원본 데이터를 저장합니다.</li>
<li>모든 코드가 언패킹되면 원 진입점에서부터 프로그램이 다시 시작됩니다. 원 진입점은 언패킹되기 이전 실행 파일의 진입점입니다.</li>
</ol>
<p>UPX(Ultimate Packer for Executables)는 현재 가장 많이 사용하는 실행 압축 프로그램입니다.
Themida는 적용된 옵션마다 언패킹 방법이 다르기 때문에 풀기 어려운 강력한 프로텍터 중 하나입니다. 많은 악성코드가 Themida로 프로텍팅되어 있고 국내 많은 보안 프로그램이나 게임들이 실행 파일을 리버싱으로부터 보호하기 위해 Themida를 사용합니다.</p>
<h1 id="2-upx-패커">2. UPX 패커</h1>
<p>UPX는 오픈 소스 실행 압축 파일입니다. 윈도우 뿐 아니라 리눅스, 유닉스 등 다양한 운영체제를 지원하고 있습니다.
UPX는 압축과 해제에 효율적인 알고리즘을 제공하고 있으며 압축 효율도 높습니다.
UPX를 다운 받고 전에 해보았던 abex1.exe파일을 upx.exe가 있는 위치로 옮겨줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8a485d19-413e-464f-8db1-71b163030b2e/image.png" alt="">
그리고 cmd에서 upx.exe 파일 위치로 가서 패킹을 해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/3d94e698-fd40-4c3a-99b6-eee4d646e14c/image.png" alt=""></p>
<blockquote>
<p>upx -o &#39;패킹 후 파일이름&#39; &#39;패킹 전 파일이름&#39;</p>
</blockquote>
<p>Ratio는 원본 파일에 대한 출력 파일의 압축 비율입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/04b3bbb0-0a41-4dbe-a68b-91df896aac76/image.png" alt="">8KB였던 파일이 7KB가 되었습니다.</p>
<p>이제 Detect It Easy로 파일을 열어보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/3d3db13f-ef84-436e-bf02-4efac4211789/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/87a6d036-fd74-4fd7-a2a9-21d0d4f5683e/image.png" alt=""></p>
<p>우선 패킹하기 전의 파일 정보로 델파이로 작성된 프로그램으로 엔트리 포인트와 정상적인 PE파일 구조와 섹션을 나타내고 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/0ff96bdb-e8dc-446a-abe9-ad463c5b3cd8/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/18f5cb5a-0cda-4923-b424-1aa8bec5eb8d/image.png" alt="">
다음은 UPX로 패킹된 프로그램의 정보입니다.
우선 UPX로 패킹되었고 프로그램의 크기가 줄어들었습니다. 또한, 정상적인 PE파일의 구조가 아닌 섹션이 UPX0, UPX1, .rsrc 만 존재합니다. UPX0에는 언패킹된 코드가 존재하는 곳입니다.
또한 엔트리 포인트도 기존의 401000이 아닌곳으로 변경되었습니다.</p>
<h1 id="3-언패킹과-oeporiginal-entry-point">3. 언패킹과 OEP(Original Entry Point)</h1>
<p>엔트리 포인트는 프로그램의 제어권이 운영체제에서 사용자 코드로 넘어가는 부분입니다.
Address Of Entry Point + Image Base 의 값이 실질적인 엔트리 포인트의 주소입니다.
패킹된 프로그램은 프로그램이 종료되면 언패킹된 코드가 메모리 어딘가에 저장되어있습니다.
(왜냐하면 프로그램 진행 중 한줄 씩 언패킹하여 실행하므로)
이제 수동으로 언패킹 과정을 봐보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/07a7f9d0-cd3b-4adc-8ddf-c4c947e41886/image.png" alt=""></p>
<p>가장 처음 <strong>PUSHAD</strong>는 모든 레지스터 값을 스택에 백업하는 동작을 수행합니다.
그 후 언패킹 코드가 따라옵니다. 이 때 압축된 데이터를 풀어 메모리 특정 영역에 저장합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/35ea6ce5-c8f0-4b08-97b9-d99a28691258/image.png" alt="">
언패킹이 모두 완료되면 <strong>POPAD</strong>명령으로 레지스터 값들을 모두 복구시켜줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/cf55c08e-5165-4db8-b147-1dd682c61cda/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/813f7181-a303-4bc7-baad-3d241833c7e8/image.png" alt=""></p>
<p>그 후에 OEP (00401000)으로 점프하여 오리지널 코드를 실행합니다.
이 때 OEP아래에 오는 코드들이 <strong>패킹되기 전의 원본 코드</strong>를 볼 수 있습니다.
이제 프로그램 덤프를 위해 OllyDumpEx 플러그인을 설치해주겠습니다.
그리고 프로그램 덤프 시 다음과 같은 창이 뜹니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/abc60a00-b621-48ed-a6be-3e62ae97df5f/image.png" alt="">
밑줄 친 부분은 패킹된 프로그램의 엔트리 포인트로 MUP방법이므로 직접 수정을 해주어야 합니다.
앞서 찾은 엔트리 포인트인 00001000을 입력해주고 덤프를 하여 파일을 만들어줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2968f79f-c715-4410-b377-5fa5775d0fa1/image.png" alt="">
덤프 파일 실행 시 오류가 발생합니다.
이유는 윈도우 프로그램의 경우 DLL형태의 시스템 라이브러리를 사용하는데 어떤 함수에서 어떤 DLL을 불러와 사용할지 저장하는 IAT테이블이 손상되었기 때문입니다. 
덤프 과정에서 쉽게 IAT테이블은 손상됩니다.
그러므로, IAT테이블의 정보를 알맞게 갱신해주어야 합니다.</p>
<p>DIE에서 덤프 파일을 열고 Import를 누르면 IAT테이블의 상태를 보여주는 화면이 나옵니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f4c73aab-a666-4f33-9814-8980907d4401/image.png" alt="">
이것은 원본 abex1.exe파일의 IAT 테이블로 GetDriveTypeA함수를 사용하는 것을 알 수 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/bbc576c1-539c-410e-87f0-d61c43856b72/image.png" alt="">
이것은 덤프 파일의 IAT 테이블로 값이 지정되어 있지 않습니다.
손상된 IAT 파일을 복구하려면 PE헤더 정보를 분석해서 사용하는 DLL과 함수에 대한 정보를 일일이 맞추어야 합니다.
IAT 복구시 사용하는 LoadPE 프로그램으로 IAT 복구를 해보겠습니다.</p>
<h1 id="4-loadpe">4. LoadPE</h1>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/b94ecdba-b704-434a-b21e-53e1519d4580/image.png" alt="">
Rebuild PE 클릭 후 IAT를 복구할 파일을 선택합니다.
복구 후 올리디버거로 덤프 파일을 열면 언패킹된 프로그램 파일을 디버깅할 수 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7f8c46ff-4845-4816-a157-c4648ed1a67e/image.png" alt=""></p>
<p>악성코드 분석의 기초인 언패킹 과정을 MUP 방식으로 해보았습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-ie045fhd</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-ie045fhd</guid>
            <pubDate>Tue, 23 May 2023 09:22:02 GMT</pubDate>
            <description><![CDATA[<p>예비군과 프로젝트를 마치고 오랜만에 돌아왔습니다 ㅎㅎ</p>
<h1 id="1-15강---nagexe">1. 15강 - NAG.exe</h1>
<p><a href="https://forum.tuts4you.com/files/file/1307-lenas-reversing-for-newbies/">https://forum.tuts4you.com/files/file/1307-lenas-reversing-for-newbies/</a> 사이트를 참조하여 15강 파일을 다운 받았습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/a4cb72e9-f813-4fd4-a893-4007cb36ddc5/image.png" alt="">
압축을 풀고 files 디렉터리안에 reverseMe. Nags.exe파일을 실행했습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/69b68c13-6511-4862-b521-7ad763c50d6c/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/3f93a0bc-88e9-46f5-8091-c542ee0dbac3/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/b5b7813b-491d-44a5-9527-c42a61767fd4/image.png" alt="">
다음과 같은 순서로 프로그램이 실행되고 종료됩니다. </p>
<h3 id="1-2-콜-스택">1-2) 콜 스택</h3>
<p>콜 스택이란 프로그램에서 사용하는 서브루틴에 대한 정보를 정장하기 위한 자료구조입니다.
주요 목적은 서브루틴 간 호출 순서를 추적할 수 있는 정보를 저장하는 것입니다. 또한 서브루틴 호출 시 필요한 인수, 지역변수를 저장합니다.
콜 스택을 사용해 처음 NAG 창이 열리는 곳을 찾아보겠습니다.
우선 F9으로 실행 후 F12로 정지시키겠습니다.
그리고 위에 K를 눌러 콜 스택 창을 열어보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/35550d20-03c6-4853-9559-0a3b2634d702/image.png" alt="">
여기서 called from은 서브루틴을 호출한 코드를 의미하고 procedure는 호출되는 서브루틴을 의미합니다.
여기서 찾아야 할 것은 NAG창을 띄우는 코드를 찾아야합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8f22edfc-3dad-49df-af0e-d02b75fc69c1/image.png" alt="">
밑 줄친 부분이 서브루틴을 호출하는 곳으로 더블클릭 하면 서브루틴 호출 코드로 이동합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2b93d104-2ea3-492a-8df0-0bc7de970ea1/image.png" alt=""></p>
<h3 id="1-3-코드-케이브">1-3) 코드 케이브</h3>
<p>코드 케이브란 리버싱을 하다 보면 코드를 수정해야할 일이 생기게 되는데 기존코드보다 크기가 작다면 상관 없지만 크기가 더 크다면 문제가 생깁니다.
그래서, 작은 공간에 큰 코드를 집어넣기 위한 기술입니다.</p>
<blockquote>
<p>사용하지 않는 코드 영역에 필요한 코드를 입력하고 수정해야할 부분에는 새로운 코드가 입력된 메모리로 점프라는 코드를 넣는 방법입니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/c7540d27-ed46-4f65-91b0-45d81c974d70/image.png" alt="">
앞서 본 0042039A 위의 부분을 보면 TEST 명령어가 있습니다. TEST는 두 값을 AND연산하여 0일시 제로 플래그에 1을 설정, 아니면 0을 설정하는 명령어입니다.
이어서 오는 JE는 점프문으로 제로플래그가 1이면 지정한 주소로 점프하고 아니면 점프하지 않는 명령어입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/0a2ef350-db79-4e28-93a6-25c7efb88f3c/image.png" alt="">
즉, EAX가 0이면 제로 플래그가 1이되어 NAG창을 띄우는 서브루틴을 스킵하여 주소 004203BA로 이동하고 EAX가 1이라면 제로 플래그가 0이되어 JE점프문을 스킵하고 NAG창을 띄우게 됩니다.
결론적으로, EAX레지스터에 어떤 값이 오느냐에 따라 NAG창을 여는 서브루틴을 호출할지 말지를 결정합니다.
위의 M을 누르면 메모리 맵을 볼 수 있습니다. 
<img src="https://velog.velcdn.com/images/sot_sky/post/c25dee07-3837-4466-b7cc-e4026ed34396/image.png" alt="">
Address는 메모리에서 시작 주소, Size는 데이터의 크기, Section은 PE파일의 구성요소, Access는 메모리 맵에 접근할 수 있는 특성을 나타냅니다.
<strong>(케이브 코드)코드 입력 시 R,E 권한이 있는 text영역에 작성하고 데이터 입력 시 R,W 권한이 있는 data영역에 작성하여야 합니다.</strong></p>
<h3 id="1-4-어셈블">1-4) 어셈블</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/8236fc8c-bac3-4726-82d9-01d59be58f7f/image.png" alt="">
코드와 메모리 영역을 내리다 보면 프로그램에서 사용하지 않는 부분이 있습니다.
일단 수정할 부분에 점프할 명령어로 수정을 합니다. 아까 본 TEST 부분을 말이죠.
<img src="https://velog.velcdn.com/images/sot_sky/post/7c4e5c33-2fc8-4fbe-8f03-c34c03047e73/image.png" alt="">
Fill with NOP&#39;s 옵션은 새로 들어가는 명령어가 기존 명령어보다 작은 경우 남는 공간을 아무 동작도 하지 않는 NOP 명령어로 채워준다는 의미로 3개의 NOP 명령이 추가 되었습니다.
기존에 있던 JE명령어도 NOP로 교체되었습니다.
TEST가 사라지면서 의미가 없는 명령이 되었기 때문이죠.
원래 총 8바이트였지만 JMP 00437D6A -&gt; E9 EE790100이 되어 5바이트가 필요해 나머지 3바이트는 NOP으로 채운것입니다.
이제 엔터를 눌러 코드를 작성할 00437D6A로 이동하고 브레이크 포인트를 걸어줍니다.
그리고 코드를 작성해줍니다.</p>
<pre><code>1. ADD BYTE PTR DS:[445E90], AL
2. CMP BYTE PTR DS:[445E90], 2
3. JNE 004203BA
4. LEA ECX, [ESP+4C]
5. JMP 0042037F</code></pre><p><img src="https://velog.velcdn.com/images/sot_sky/post/25fc3a3a-96f0-4af6-9242-b69c64977ff9/image.png" alt="">
<strong>1. 특정 메모리 영역에 1바이트 단위 덧셈</strong>
-&gt; AL은 EAX의 하위 8비트를 의미합니다. 프로그램에서 창을 띄울 때만 EAX가 1로 설정되기 때문에 명령어를 수행한 결과는 주소 445E90에 몇 번째 띄워지는 창인지 순서가 기록됩니다.</p>
<p><strong>2. 특정 메모리 영역에 2가 저장되었는지 확인</strong>
-&gt; 주소 445E90에 2가 저장되었는지 확인하고 맞다면 제로 플래그가 1이 설정됩니다. 
창이 두번째 창을 띄우는지 검사하기 위한 용도의 명령입니다.</p>
<p><strong>3. 두 번째 창이 아니면 창을 띄우지 않는 로직으로 이동</strong>
-&gt; 두 번째 창인 경우에만 제로 플래그가 1이 되므로 JNE 명령어로 제로 플래그가 1이 아닌경우는 창을 띄우지 않는 004203BA로 이동시킵니다.</p>
<p><strong>4. 점프 명령어 입력을 위해 삭제된 원본 코드 입력</strong>
-&gt; TEST 부분을 수정할 때 비교와 점프 구문은 단순 비교연산이므로 없어도 되지만 그 후에 나오는 LEA 명령은 프로그램에 필요한 명령이므로 캐이브 코드안에 그대로 넣어줍니다.</p>
<p><strong>5. 두 번째 창이면 창을 띄우는 로직으로 이동</strong>
-&gt; CMP연산에 의해 제로 플래그가 1이되면 JNE를 스킵하고 이어가므로 여기서 창을 띄워주는 서브루틴 0042037F를 호출합니다.</p>
<h3 id="1-5-파일-저장">1-5) 파일 저장</h3>
<p>프로그램을 재시작하면 변경사항이 모두 사라집니다.
그래서 코드 부분을 우클릭 후 copy to executable -&gt; all modifications을 눌러주면 
변경 사항이 모두 나오게되는데 여기서 또 우클릭 후 save file을 하면 변경사항이 저장된 프로그램을 저장할 수 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8d4f6e7e-6ba4-4f8b-8c2b-07cd91424a6c/image.png" alt="">
실행 시 NAG창이 더이상 뜨지 않습니다.</p>
<h1 id="2-17강---keygenme">2. 17강 - KeygenMe</h1>
<h3 id="2-1-프로그램-동작-방식">2-1) 프로그램 동작 방식</h3>
<p>우선 전에 풀었던 일련번호 문제와 비슷한 것 같습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/856233e7-fd9b-4592-8ab9-2ff8a84dd871/image.png" alt="">
로그인 체크 창이 있고 입력 시 
<img src="https://velog.velcdn.com/images/sot_sky/post/5faf24f9-cd57-40b3-b446-1395180fa3f9/image.png" alt="">
오류가 나옵니다.
목적은 일련번호를 알아내는 것이지만 프로그램 내부 메모리 어딘가에 적혀있다면 쉽게 찾아낼 수 있습니다. 그러나 일련번호가 동적으로 생성된다면 프로그램을 분석해야합니다.</p>
<h3 id="2-2-디버깅">2-2) 디버깅</h3>
<p>올리디버거로 실행시키고 문자열에서 에러메시지를 찾아보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8c8fd39b-38b8-4f21-8397-a0743577e4b3/image.png" alt="">
여러 문자열들이 보이네요. 그중에 성공 메시지로 추정되는 메세지가 보입니다. That&#39;s right ~~
일단 에러 메세지의 코드 부분으로 들어가보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ecfb0db7-48bf-43c6-85da-96976e18c953/image.png" alt="">
MessageBoxA 함수를 호출합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/cf73c2c9-2b6b-4820-954e-1f1358e69a06/image.png" alt="">
위로 조금 올라가면 분기문이 나옵니다. -&gt; 성공과 실패를 가르는 분기문이죠.
CMP연산 후 제로 플래그가 1이 아니면 00401353(에러 메세지)으로 점프를 하게 되있네요.
즉, 제로 플래그를 0으로 만들어주면 되지만 학습이 목적이므로 일련번호 생성과정을 알아야겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/5ae9945f-9b2d-4f75-8dd9-104339d6f098/image.png" alt="">
좀 더 위로 가면 lsrtlen함수를 호출합니다. 문자열의 길이를 EAX에 넣어주는 함수입니다.
그 후 ECX에 EAX값을 넣어주고 반복횟수로 ECX를 사용합니다. 즉 문자열의 길이만큼 반복을 한다는 뜻이죠.
<img src="https://velog.velcdn.com/images/sot_sky/post/ff2f3684-5cb6-4a67-a9aa-8cda1d785fa7/image.png" alt="">
그 후에 403138의 값과 ESI와 비교하여 성공과 실패를 분기합니다.</p>
<h3 id="2-3-동적-분석">2-3) 동적 분석</h3>
<p>이제 아이디에 lena151 일련번호 1234를 입력해서 동작을 살펴보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/88f12182-864d-4735-95c8-e8ed210cc183/image.png" alt="">
lstrlen에 브레이크를 걸고 실행 시 입력한 아이디가 들어가있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/4099d1d2-c352-42bc-8c95-5b4d70fcad24/image.png" alt="">
lstrlen함수를 마치자 EAX에 아이디의 길이인 7이 들어가있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7e2bbbfd-b846-4bd1-9b03-ecc0ff8cd810/image.png" alt="">
이제 반복문을 천천히 따라가 보겠습니다.</p>
<blockquote>
<ol>
<li>메모리 403038위치에서 4바이트 만큼을 EDX로 복사합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/dc8324a5-2444-42b3-ba6a-a84350d598b8/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/bddc8d99-ca9f-4cee-bdbe-8c85d3e663f6/image.png" alt="">
&amp;nbsp</li>
<li>EAX+403037=403038의 위치의 메모리 값의 1바이트만큼을 EDX의 하위 1바이트인 DL에 복사합니다.<img src="https://velog.velcdn.com/images/sot_sky/post/f2e587fb-0894-4a71-b8c6-64b4a4510bb1/image.png" alt="">
&amp;nbsp</li>
<li>EDX 와 0FF를 AND연산하여 EDX에 저장합니다
즉, EDX의 하위 2바이트를 제외하고 모두 0으로 변경
<img src="https://velog.velcdn.com/images/sot_sky/post/13127b6a-018f-4a47-aeb9-2d993c41ab05/image.png" alt="">
&amp;nbsp</li>
<li>EDX값을 EBX로 복사합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d876e077-6e2a-42d1-9738-3ab4a033612c/image.png" alt="">
&amp;nbsp</li>
<li>IMUL은 부호 있는 곱셈으로 (부호 없는 곱셈은 MUL) EBX와 EDX를 곱해주고 EBX에 저장합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/0399872f-9e2d-4380-846e-46de3fe171c1/image.png" alt="">
&amp;nbsp</li>
<li>ESI값에 EBX값을 더해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/360e65ce-5ea7-4b2f-9ad9-4f381a80eb94/image.png" alt="">
&amp;nbsp</li>
<li>EDX값을 EBX로 복사해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/3110ff56-9c89-49ab-8450-6a8e12e65eea/image.png" alt="">
&amp;nbsp</li>
<li>SAR 명령어는 두 번째 인자만큼 첫번째 인자를 오른쪽으로 시프트 연산을 해줍니다.
즉, 비트를 한칸씩 옮겨 2로 나눠주는 효과를 나타냅니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f89b16ec-4167-4d9e-b729-a899134c7d82/image.png" alt="">
&amp;nbsp</li>
<li>EBX에 3을 더해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/07bac9bf-51c5-4e46-8c2a-bcee84060e34/image.png" alt="">
&amp;nbsp</li>
<li>EBX와 EDX를 곱하고 EBX에 값을 저장합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/12b4dc4a-b9a1-43a0-87e9-11692836efb3/image.png" alt="">
&amp;nbsp</li>
<li>EBX에서 EDX를 빼줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/522c8489-d11a-46d8-8def-947efca20d2c/image.png" alt="">
&amp;nbsp
12,13. ESI에 EBX를 더해주고 ESI에 ESI를 더해줍니다
<img src="https://velog.velcdn.com/images/sot_sky/post/f11f6df7-b0f2-4dc8-8fde-099167ab42b5/image.png" alt="">
&amp;nbsp
14, 15. EAX에 1을 더해주고 ECX에 1을 빼줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d7ddc094-c122-48b9-ac52-4f85f7c0f8ac/image.png" alt="">
&amp;nbsp
16 .  제로 플래그가 1이면 스킵하고 1이 아니라면 다시 반복합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/64a01c53-6e83-4578-8074-3c102ceb3f22/image.png" alt=""></li>
</ol>
</blockquote>
<p>이제 두 번째 반복문에서 일련번호 생성을 살펴보겠습니다.
일단 MOV DL,BYTE PTR DS:[EAX+403037]에서 다음에 사용할 문자열 값을 가져옵니다.
이번에는 EAX값이 2이므로 lena151에서 &#39;e&#39;값을 가져오게 됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d8ec026a-c030-4e0d-8f8f-63e6520204b6/image.png" alt="">
DL의 값이 65로 바뀌었습니다.</p>
<p>지금까지 덧셈, 뺄셈, 곱셈 등 과정을 거치며 난독화 과정을 보았습니다. 이러한 난독화는 상용 프로그램에서 기본적으로 사용되며 익숙해져야 합니다.
이제 반복문을 빠져나와 ESI를 보면 생성된 일련번호가 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/b99ede84-6d06-46e3-8ebb-1b6e5be790ce/image.png" alt="">
3EF552값을 아스키 코드로 변경한 것이 일련번호입니다.
인터넷에 변환기가 바로 나오지만 코드 케이브 기술로 직접 변환해보겠습니다.</p>
<h3 id="2-4-아스키-코드-변환">2-4) 아스키 코드 변환</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/2e0f35e4-42f5-4e1b-b21e-3de5efc1aa7c/image.png" alt="">
우선 점프문을 제가 원하는 임의의 주소로 무조건 점프하도록 변경시키겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ce4b9385-3981-4fc7-9399-feb8a4889645/image.png" alt="">
이제 00401385 코드 부분에 코드를 작성합니다.</p>
<pre><code>1. MOV DWORD PTR DS:[403148],ESI
2. JNZ SHORT 00401353
3. JMP SHORT 0040133E</code></pre><ol>
<li>일련번호가 저장된 ESI 레지스터를 403148 메모리 주소에 저장시켜 아스키 코드로 변환된 일련번호를 확인할 수 있습니다.</li>
<li>만약 일련번호와 입력 값이 틀리다면 00401353으로 이동합니다.</li>
<li>두 개의 일련번호가 일치하다면 0040133E로 이동합니다.</li>
</ol>
<h3 id="2-5-프로그램-저장-실행">2-5) 프로그램 저장 실행</h3>
<p>아까와 마찬가지로 재실행시 변경사항이 초기화되므로 파일을 따로 저장하고 실행을 해줍니다.
브레이크 포인트를 걸고 실행 후 메모리 값을 확인했습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/441d1814-2b04-4c70-bd4b-3519dd831729/image.png" alt="">
이렇게 나와있어서 인터넷 홈페이지에서 돌려보니 &#39;Rõ&gt;&#39; 이것이 나왔습니다.
그래서 프로그램 일련번호에 넣어보니 특수문자 입력되지 않아 여기까지만 했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[20230517]]></title>
            <link>https://velog.io/@sot_sky/20230517</link>
            <guid>https://velog.io/@sot_sky/20230517</guid>
            <pubDate>Wed, 17 May 2023 14:32:39 GMT</pubDate>
            <description><![CDATA[<p>이번주는 예비군을 갔다왔다.... 그래도 출퇴근으로 해서 다행인것 같다 ㅎㅎ
이제 내일 하루 남았다 ㅎㅎ
날씨가 갑자기 더워져서 훈련받을때 너무 덥다..... 가보니 반가운 얼굴들이 많았다.
애들이랑 만나서 오랜만에 얘기도하고 유일한 낙이었다
예비군 갔다오면 씻고 바로 기절해버린다 ㅋㅋㅋ 근데 진짜 꿀잠자는건 오랜만이라 좋았다,.
근데 어제는 자는데 모기때문에 진짜 죽는줄;;
두번 깨고 너무화나서 불키고 총 3마리 잡고 잤다 근데 일어나니까 오른팔에 세보니 10방 물려있었다;;
진짜 모기좀 박멸시키고 싶다 제발좀
어쨋든 이번주 처음부터 다시 상기해보자면 처음 예비군 훈련이어서 조금 설렜는데 너무 더워서 바로 고통으로 변화됐다 ㅋㅋㅋ
둘째날까지는 같이 간 친구가 없어서 모르는 사람들이랑 같은 조를 했었다
모르는 사람들이랑 얘기도 많이 해보니 결국 그냥 다 나 같은 사람들이어서 거리감이 없어서 편하고 좋았다 ㅋㅋㅋ
첫째날은 훈련 거의 끝날때 쯤 알고보니 내 앞번호 사람이 중학교 동창이었다 ㅋㅋㅋㅋ 진짜 신기했다
그리고 오늘 셋째 날엔 어제 훈련중에 만난 고등학교 동창이랑 같이 갔다.
친구가 차가 있어서 편했다 ㅎㅎ 고맙다 친구야
내일 마지막까지 끝내고 다시 공부에 집중해야겠다! ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-hxzypa50</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-hxzypa50</guid>
            <pubDate>Wed, 10 May 2023 13:51:54 GMT</pubDate>
            <description><![CDATA[<h1 id="1-abex-crackme---4">1. abex crackme - 4</h1>
<h3 id="1-1-프로그램-분석">1-1) 프로그램 분석</h3>
<p>detect it easy로 파일을 먼저 열어보면
<img src="https://velog.velcdn.com/images/sot_sky/post/804367c7-9bc6-4a22-a237-53320e723182/image.png" alt="">
비주얼 베이직으로 작성된 프로그램임을 알 수 있습니다.
비주얼 베이직은 MSVBVM60.DLL에 있는 API를 호출하면서 동작을 수행합니다.
따라서 내부적으로 호출되는 API를 검색해보면 대부분 MSVBVM60.DLL에 속해있는 것을 알 수 있습니다.
프로그램을 실행해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/1399eb56-140e-42a6-a875-ad7197fd53ca/image.png" alt="">
일련번호 입력 창이 뜨는데 아무 값이나 넣어보아도 Register 클릭 창이 활성화 되지 않는것으로 보아 특정 값 입력 시 활성화되는 것으로 보입니다.</p>
<h3 id="1-2-프로그램-구조-분석">1-2) 프로그램 구조 분석</h3>
<p>일단 어떠한 알림 창도 뜨지 않아 문자열을 통한 검색을 어려울 것 같습니다.
그래서 호출된 API목록을 확인해보겠습니다. 코드 우클릭 -&gt; search for -&gt; all intermodular calls 클릭
<img src="https://velog.velcdn.com/images/sot_sky/post/5b5d0dca-e303-41c8-a870-e41434477f98/image.png" alt=""></p>
<p>다음과 같이 호출된 MSVBVR60.DLL에서 호출한 API목록이 나옵니다.
여기서 생각을 해보면 일련번호는 문자열의 비교를 통해 Register 버튼을 활성활 할 것입니다.
그래서 문자열 비교와 관련된 API인 vbaStrCmp를 찾아 위치로 가보았습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/83b9bbf6-13a1-46ba-95bb-bc0f1e5e2edb/image.png" alt="">
__vbaStrCmp() 함수 호출 전에 EAX, ECX를 스택에 push하는 것으로 보아 함수의 인자를 넣어준것 같습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/be3b6bd3-f09c-412e-9259-7f91d27e6c56/image.png" alt="">
그리고 밑에 연산 후 분기점에서 분기되어 버튼을 활성화 시키거나, 활성화를 시키지 않습니다.
그래서 마지막 TEST 부분에서 브레이크를 걸어 분기를 바꿔보겠습니다.
TEST 명령어는 AND연산을 하여 단순히 값이 0인가 아닌가를 판별하고 보통 분기문과 같이 쓰입니다.
만약 DI가 0이라면 AND연산 시 0이 되어 따로 값은 저장을 하지 않고 ZF만 1로 설정해줍니다.
그래서 밑에 JE분기문에서 ZF가 1이므로 지정된 주소로 점프를 합니다.
그래서 분기가 되기전 ZF값을 0으로 바꾸어 분기가 되지 않도록 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/b8ffa0a9-94b9-41de-814c-16c44f66d137/image.png" alt="">
분기되지 않고 이어가고 있습니다.
이어서 프로그램 실행 후 활성화 된 Register 버튼 클릭
<img src="https://velog.velcdn.com/images/sot_sky/post/d4aaa74a-736e-4e2c-b79d-690e266d0255/image.png" alt=""></p>
<h3 id="1-3-일련번호-찾기">1-3) 일련번호 찾기</h3>
<p>이번에는 일련번호를 직접 찾아보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/29a7024c-a6eb-4df1-a87b-bd7f85d31640/image.png" alt="">
이 부분을 보면 분기 이전에 DI값을 TEST 명령을 합니다.
그리고 DI값에 의해 성공과 실패의 분기를 나누게 됩니다.
올라가다 보면 EDI 값은 EAX값을 복사한 것입니다.
그위에는 __vbaStrCmp()함수의 결과가 EAX에 저장이 되었죠.
그러므로 __vbaStrCmp함수의 인자 즉, 호출 이전의 EAX와 ECX의 값을 보아야 합니다.
그래서 0040230B에 브레이크를 걸고 실행 시 
<img src="https://velog.velcdn.com/images/sot_sky/post/ad55ceae-404e-4217-bad3-7bc19adc9134/image.png" alt="">
EAX 값의 덤프 메모리는 9를 나타내고 ECX는 ASCII로 2215185를 나타냅니다.
즉, 문자열 비교 함수에 9와 2215185가 인자로 사용된다는 것이죠.
그래서, 우리는 추측을 할 수 있습니다. 2215185가 일련번호라는 것을 말이죠.
그래서 브레이크 문을 모두 제거하고 프로그램 실행 후 2215185를 넣어보았습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/c0228d5d-2bef-4d30-9759-f5257d2e4ca4/image.png" alt="">
성공 메세지가 뜨네요</p>
<h1 id="2-abex-crackme---5">2. abex crackme - 5</h1>
<h3 id="2-1-함수-분석">2-1) 함수 분석</h3>
<p>해당 프로그램도 마찬가지로 실행 시 일련번호를 물어봅니다.
그런데 check버튼이 활성화 되어있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/8dd59746-9c19-42fd-ae99-2ed05c0dfae5/image.png" alt="">
그래서 체크 버튼을 누르면
<img src="https://velog.velcdn.com/images/sot_sky/post/80419a84-60f4-4e21-a056-9709566c85f0/image.png" alt="">
다음과 같이 에러 창이 뜨고 프로그램이 종료됩니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d54ea121-190d-41ff-8d8e-889a3ca086c3/image.png" alt="">
일단 호출한 API목록을 보면 새로운 것들이 있습니다.
DialogBoxParamA()함수, GetVolumeInformationA()함수
GetVolumeInformationA()함수는 지정된 루트 디렉터리가 속한 파일 시스템 정보와 볼륨 정보를 가져오는 함수입니다.</p>
<pre><code>BOOL GetVolumeInformationA(
  [in, optional]  LPCSTR  lpRootPathName,
  [out, optional] LPSTR   lpVolumeNameBuffer,
  [in]            DWORD   nVolumeNameSize,
  [out, optional] LPDWORD lpVolumeSerialNumber,
  [out, optional] LPDWORD lpMaximumComponentLength,
  [out, optional] LPDWORD lpFileSystemFlags,
  [out, optional] LPSTR   lpFileSystemNameBuffer,
  [in]            DWORD   nFileSystemNameSize
);</code></pre><p>MSDN에 정의된 내용입니다.
in은 입력 변수, out은 함수의 실행결과가 저장된 변수를 지정하는 것입니다.
optional은 선택적으로 사용할 수 있음을 의미합니다.
예를 들어, lpRootPathName은 선택적으로 사용할 수 있지만 nVolumeNameSize 입력 변수는 필수적으로 사용되어야 합니다.</p>
<p>DialogBoxParamA()함수는 대화 상자 템플릿 리소스에서 모달 대화 상자를 만듭니다. 이 값으로 대화상자의 모형을 초기화할 수 있습니다.</p>
<pre><code>INT_PTR DialogBoxParamA(
  [in, optional] HINSTANCE hInstance,
  [in]           LPCSTR    lpTemplateName,
  [in, optional] HWND      hWndParent,
  [in, optional] DLGPROC   lpDialogFunc,
  [in]           LPARAM    dwInitParam
);</code></pre><p>비슷하게 DialogBoxA()함수가 있습니다. 이 함수는 대화 상자를 만들어줍니다.</p>
<pre><code>void DialogBoxA(
  [in, optional]  hInstance,
  [in]            lpTemplate,
  [in, optional]  hWndParent,
  [in, optional]  lpDialogFunc
);</code></pre><h3 id="2-2-반복문을-통한-문자열-변경">2-2) 반복문을 통한 문자열 변경</h3>
<p>GetVolumeInformationA()함수 밑을 보면 반복문이 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/179c13e4-5e69-4e91-b33e-1944550d1b5f/image.png" alt="">
특정 코드에서 뒤로 점프하여 반복적으로 수행하는 부분을 반복문으로 예상할 수 있습니다.
004010AD를 브레이크로 잡은 후 스텝 오버하여 천천히 보면 반복문의 구조를 파악할 수 있습니다.</p>
<blockquote>
<h3 id="step-1">step 1</h3>
<p>EDX의 하위 16비트 값인 DL에 2를 넣어줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/80411aef-d599-477e-8d77-91d57b72f514/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/486a4ccb-4453-4e83-bbb2-069c9d220db9/image.png" alt=""></p>
</blockquote>
<h3 id="step-2">step 2</h3>
<p>40225C, 40225D, 40225E, 40225F의 메모리 값에 각각 1씩 더해줍니다
<img src="https://velog.velcdn.com/images/sot_sky/post/ad8471ea-8de0-461d-97b7-223cb9274122/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/b36f3f5b-a1e2-4380-9ae0-9f5c92781f01/image.png" alt=""></p>
<h3 id="step-3">step 3</h3>
<p>DL을 1감소 시킵니다
<img src="https://velog.velcdn.com/images/sot_sky/post/a90f7867-b962-4901-8cd1-7383a7ba82e8/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/74438f57-e498-46d8-a5cc-84517fd0d33e/image.png" alt=""></p>
<h3 id="step-4">step 4</h3>
<p>JNZ, 즉 ZF가 1이 아닌경우 004010AF로 점프합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/31da3d49-0e8c-4173-b78d-35fab0f6a722/image.png" alt="">
DL 값은 1이 감소하고 다시 반복 시작.</p>
<h3 id="step-5">step 5</h3>
<p>두번째 분기에선 DL이 0이되어 ZF가 1이 설정되었으므로 JNZ를 건너뜁니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f43ad6bc-ce83-4eb9-b9f5-3ebdd6e24ec1/image.png" alt=""><img src="https://velog.velcdn.com/images/sot_sky/post/9454d616-8bc4-4a32-88ac-b65ed875cea3/image.png" alt=""></p>
<p>즉, 고급 언어로 작성해보자면</p>
<pre><code>for(int i = 2; i &gt; 0; i--){
    &lt;반복할 코드&gt;
    }
    return 0;</code></pre><p>여기서 i는 DL의값으로 추측됩니다.</p>
<h3 id="2-3-프로그램-분석">2-3) 프로그램 분석</h3>
<p>실패 메시지 아래 성공 메세지 함수 부분이 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2a59d742-04a5-4651-b22b-c0923c971884/image.png" alt="">
그렇다면 어딘가에서 분기문에 의해 실패메세지로 가지않고 성공메세지의 호출인 00401117로 분기되는 곳이 있을 것입니다. 
코드를 조금 올려보니 역시 있었습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/6d4b278c-5e7c-476a-9f2b-cbd1a2e62879/image.png" alt="">
lstrcatA()함수는 MSDN에서 찾아보니 보안에 취약하므로 StringCchCat()함수를 사용하라는 권고문이 있었습니다. 아마 버퍼오버플로우에 의한 취약점이 있는것으로 판단됩니다.
문제에서는 lstrcat()함수가 쓰였으니 우선 알아보겠습니다.
한 문자열을 다른 문자열에 추가하는 함수입니다.</p>
<pre><code>LPSTR lstrcatA(
  [in, out] LPSTR  lpString1,
  [in]      LPCSTR lpString2
);</code></pre><p>다음으로 lstrcmpiA()함수는 대/소문자를 구분하지 않고 두 문자열을 비교합니다.</p>
<pre><code>int lstrcmpiA(
  [in] LPCSTR lpString1,
  [in] LPCSTR lpString2
);</code></pre><p>문자열이 다르면 반환 값은 1, 문자열이 같으면 반환 값은 0입니다.
반환 값은 EAX에 저장됩니다.</p>
<p>이제 프로그램의 전체적인 분석을 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/1f9daa35-afbe-4569-b93a-6e217ef78d64/image.png" alt=""></p>
<ol>
<li>루트 디렉터리의 파일 시스템 정보와 볼륨 정보를 가져와 일련번호를 만들기 위한 입력 값으로 사용</li>
<li>파일 시스템 정보와 프로그램 내부에 들어 있는 문자열을 결합해서 만든 새로운 문자열을 반복문을 통해 다른 문자열로 변형 =&gt; 일련번호 생성 과정</li>
<li>앞에서 생성한 새로운 문자열을 가지고 프로그램 내부에 들어 있는 문자열과 결합해 일련번호 생성.</li>
<li>생성한 일련번호와 사용자가 입력한 값이 맞는지 확인. =&gt; 맞으면 성공 메세지 틀리면 오류 메세지</li>
</ol>
<h3 id="2-4-문제-해결">2-4) 문제 해결</h3>
<blockquote>
<h3 id="step-1-1">step 1</h3>
<p>일단 파일 시스템 정보를 가져오는 부분부터 차례대로 스텝 오버 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/4cb58216-5c0a-4146-9559-de13a3bb7456/image.png" alt="">
0040225C에 있는 데이터는 일련번호를 만드는데 사용되기 때문에 메모리에 어떤 값이 저장되는지 보아야 합니다. 그래서 immediate constant 후 해당 함수를 끝내면
<img src="https://velog.velcdn.com/images/sot_sky/post/06020693-5e83-492c-99e6-dd8778504202/image.png" alt="">
아무 내용이 없었습니다.
다음에 나오는 함수는 앞서 설명한 lstrcatA() 함수입니다. 이 함수는 인자 2개인 문자열을 결합해주는 함수입니다. 
<img src="https://velog.velcdn.com/images/sot_sky/post/3afc8e2a-722d-4ab6-8fa8-ce415b06b19f/image.png" alt="">
해당 함수를 마친 후 0040225C메모리의 값 확인
<img src="https://velog.velcdn.com/images/sot_sky/post/4f645987-e267-4c6e-b268-0c77bf31f0b8/image.png" alt="">
제가 보기엔 처음 파일 시스템 정보를 가져오는 곳에서 이유는 모르겠지만 공백을 가져오고 공백과 4562-ABEX를 결합한 것 같습니다.</p>
</blockquote>
<h3 id="step-2-1">step 2</h3>
<p>이제 반복문 부분입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/2ff294da-a4e8-4c19-83e6-977894c37de5/image.png" alt="">
총 2번 반복하고 메모리의 40225C ~ 40225F까지 의 값들을 1씩 증가시킵니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/7a01930a-e292-4e4f-8ad6-89296efc5611/image.png" alt="">
반복문 수행 후 변경된 메모리 값</p>
<h3 id="step-3-1">step 3</h3>
<p>다음은 마지막으로 문자열을 또 결합해줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/5c11fb9d-d2d6-4be7-afc8-e48675444708/image.png" alt="">
lstrcmpiA() 함수 전까지 수행을 하자
<img src="https://velog.velcdn.com/images/sot_sky/post/79ddf425-3376-4125-b756-66df4b54a047/image.png" alt="">
EAX에 저장된 일련번호는 다음과 같았습니다.</p>
<h3 id="step-4-1">step 4</h3>
<p>이제 lstrcmpA 함수를 해석해보면
<img src="https://velog.velcdn.com/images/sot_sky/post/903d9b8e-71f8-4945-8b5b-3b86db6ef392/image.png" alt="">
제가 입력한 &quot;abcd&quot;문자열과 일련번호인 &quot;L2C-57816784-ABEX&quot;을 비교합니다.
lstrcmpA함수는 비교된 문자열이 일치하다면 EAX에 0을 저장하고 일치하지 않으면 1을 저장합니다.
지금같은 경우는 일치하지 않으므로 EAX에는 1이 저장될 것입니다.
그러므로 CMP EAX, 0 연산을 통해 ZF는 0으로 유지되고(EAX-0이 0이 아니므로) JE 분기문에서 ZF가 0이므로(EAX와 0이 같지 않으므로) 성공 메세지로 가는 주소 00401117로 가지 못하고 그대로 이어가게 되어 실패 메세지가 나옵니다.</p>
<h3 id="step-5-1">step 5</h3>
<p>그럼 이제 일련번호(&quot;L2C-57816784-ABEX&quot;)를 알았으므로 프로그램을 다시 시작해서 해당 값을 넣어보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/e87d95d7-517f-4f42-ac68-2bffd6ea15bd/image.png" alt="">
성공 메세지가 나왔습니다.</p>
<h1 id="3-새롭게-배운-것">3. 새롭게 배운 것</h1>
<p>detect it easy으로 프로그램 생성 환경을 알고 비주얼 베이직인 경우 대부분 MSVBVM60.DLL에서 API 호출 =&gt; __vbstrcmp()
DL은 EDX의 하위 16비트이다.
lstrcatA() 함수는 문자열을 결합시키는 함수 ##보안문제로 StringCchCat()함수 권장
lstrcmpiA() 함수는 문자열을 비교하여 같으면 EAX에 0을 저장하고 일치하지 않으면 EAX에 1을 저장해준다.
GetVolumeInformationA()함수는 지정된 루트 디렉터리의 파일 시스템 정보와 볼륨 정보를 가져오는 함수이다.
반복문은 보통 후위 코드로 점프하며 주로 암호화나 일련번호를 생성 등에 사용된다.
JE = 비교값이 같을 경우 지정 주소로 점프
CMP = 두 인자 비교하는 명령어 주로 분기문과 같이 쓰인다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-oc1sd4vw</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-oc1sd4vw</guid>
            <pubDate>Wed, 10 May 2023 08:54:49 GMT</pubDate>
            <description><![CDATA[<h1 id="1-abex-crackme---3">1. abex crackme - 3</h1>
<p>이번에는 3번째 예제를 분석해보겠습니다.</p>
<h3 id="1-1-프로그램-실행">1-1) 프로그램 실행</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/43b4503c-1232-495e-84c8-e6c2964bff1b/image.png" alt="">
프로그램 실행 시 다음과 같이 알림이 뜨고 확인을 누르면
<img src="https://velog.velcdn.com/images/sot_sky/post/5edccc85-fd3c-47be-988f-6a23cac2995b/image.png" alt="">
다음 창이 뜨고 프로그램이 종료됩니다.</p>
<h3 id="1-2-함수-호출-규약">1-2) 함수 호출 규약</h3>
<p>함수 호출 규약이란 함수를 호출할 때 인자를 전달하는 방식이나 함수 실행이 끝나고 스택을 정리하는 방식에 대한 약속입니다.
크게 3가지의 방식이 있습니다.</p>
<h4 id="1-cdecl-방식">1) cdecl 방식</h4>
<p>=&gt; 인자가 오른쪽에서 왼쪽으로 순서대로 스택으로 전달되고 함수 종료 시 호출자가 피호출자의 스택 프레임을 정리합니다.</p>
<h4 id="2-stdcall-방식">2) stdcall 방식</h4>
<p>=&gt; 인자가 오른쪽에서 왼쪽으로 순서대로 스택으로 전달되고 함수 종료 시 피호출자가 스스로 자신의 스택 프레임을 정리합니다.</p>
<h4 id="3-fastcall-방식">3) fastcall 방식</h4>
<p>=&gt; 인자가 오른쪽에서 왼쪽으로 순서대로 레지스터를 사용해서 전달되고 레지스터를 사용하므로 별도로 스택을 정리할 필요가 없습니다.</p>
<p>예시를 들어 설명해보겠습니다.</p>
<pre><code>고급 언어
void caller() {
    callee(1,2);
    return 0;
    }</code></pre><p>고급 언어가 다음과 같이 작성되있다고 해보면
cdecl 방식의 어셈블리어는 다음과 같습니다.</p>
<pre><code>PUSH 2
PUSH 1
CALL callee
ADD ESP 8</code></pre><p>즉, 스택은 후입선출의 자료구조이므로 오른쪽인자부터 넣어 꺼낼때는 왼쪽인자(1) 부터 나오게 됩니다.
또한 마지막에 사용한 스택 프레임 정리과정에서 인자 2개, 총 8바이트를 사용하였으므로 현재 ESP값에 8바이트를 더해주어 스택을 정리합니다.</p>
<p>stdcall의 경우</p>
<pre><code>PUSH 2
PUSH 1
CALL callee</code></pre><p>한 후 호출된 callee 루틴에서</p>
<pre><code>PUSH EBP
MOV EBP, ESP
.............
POP EBP
RETN 8</code></pre><p>이렇게 해줍니다.
분석해보면 마찬가지로 인자의 오른쪽부터 스택에 값을 넣어준 후 callee 서브루틴을 호출합니다.
그 후 서브루틴의 스택 영역을 할당하고 마지막에 RETN 8로 사용한 바이트 수만큼을 호출자가 아닌 피호출자가 직접 스택을 정리합니다.</p>
<p>마지막으로 fastcall의 경우</p>
<pre><code>MOV EDX 2
MOV ECX 1
CALL callee</code></pre><p>한 후 호출된 callee 루틴에서</p>
<pre><code>PUSH EBP
MOV EBP, ESP
............
MOV DWORD PTR SS:[EBP-8], EDX
MOV DWORD PTR SS:[EBP-4], ECX
............
POP EBP
RETN</code></pre><p>이렇게 해줍니다.
분석해보면 레지스터에 인자를 할당할 때는 왼쪽에 있는 1부터 순서대로 할당되고 callee함수 안에서 레지스터에 들어가 있는 값을 사용할 때는 스택에 먼저 복사한 다음에 스택에 있는 값을 사용합니다.</p>
<h3 id="1-3-부호-있는-숫자의-표현">1-3) 부호 있는 숫자의 표현</h3>
<p>윈도우에서 음수를 표현할 때 <strong>2의 보수 방식</strong>을 사용합니다. 운영체제 수업 때 배웠던 기억이 있네요.
숫자의 2진수에서 NOT연산을 한 후에 1을 더해주는 방식입니다.
예시를 들어보자면,
-4를 표현하려면 4의 이진수 값 0000 0100에서 NOT연산을 해줍니다.
그러면, 1111 1011이 되고 여기에 1을 더해주면 1111 1100이 됩니다.
이 값은 16진수로 표현하면 FC가 됩니다.</p>
<h3 id="1-4-프로그램-구조-분석">1-4) 프로그램 구조 분석</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/bf4f3cde-e47c-4389-b21f-7453801c68ce/image.png" alt="">
올리디버거로 파일을 열자 생각보다 단순한 파일이었습니다.
엔트리 포인트 아래 필요한 문자열들이 보여서 문자열을 찾을 필요가 없었습니다.
일단 분석해보자면</p>
<blockquote>
<p>MessageBoxA함수 호출로 알림창을 띄우고
CreateFileA함수로 abex.12c라는 이름의 파일이 있는지 확인합니다.
그리고 여기 분기점에서 EAX와 -1을 묵시점 빼기를 하여 결과 값이 0이면 ZF가 1이되어 다음 JE 분기점에서 지정된 주소로 점프를 합니다. 00401075에는 실패 메세지가 나옵니다.
만약 파일이 있다면, GetFilesize함수로 파일의 사이즈를 확인합니다.
여기서도 CMP로 EAX와 12(10진수로는 18) 값을 비교합니다.
그 후, JNZ 분기문으로 ZF가 1이 아닐경우 분기하지 않고 최종 성공 메세지가 나오고 ZF가 1일 경우 분기하여 실패 메세지가 나옵니다.</p>
</blockquote>
<h3 id="1-5-문제-해결">1-5) 문제 해결</h3>
<p>본격적으로 문제 해결을 해보겠습니다.
일단 CreateFileA() 함수에 대해 알아보면</p>
<pre><code>HANDLE CreateFileA(
  [in]           LPCSTR                lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);</code></pre><p>상단에 파일 이름을 지정하는 부분이 있습니다.
그러므로 코드에서 abex.l2c라는 이름의 파일이 있는지 확인을 하므로 abex crakcme3파일이 있는곳에 abex.l2c라는 파일을 하나 만들어 주겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ecea9909-218f-4162-ae38-9ff714652dcb/image.png" alt="">
이제 아마 CreateFileA 부분은 통가하겠지만 GetFilesize 함수에서 실패로 분기될 것입니다.
그러면 CreateFileA 함수 이전에 브레이크를 걸어 확인해 보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/60bd0849-bfe6-4df0-aa7b-db04e96f4d63/image.png" alt="">
역시 JE부분에서 분기되지 않고 무사히 건너왔습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/bf968cb4-9367-4538-9e65-2c205310dc88/image.png" alt="">
하지만, GetFileSize에서 역시 분기되어 실패 메세지 호출 함수로 이동하는군요.
그러면, 해당 분기점에서도 그대로 건너가게 하기위해 해당 파일 사이즈를 바꿔 주어야 합니다.
파일에 1234567890을 입력하고 실행 후 CMP이전에 EAX값을 보면
<img src="https://velog.velcdn.com/images/sot_sky/post/d1127d5d-bbd2-4c98-a13f-1c26b6fa0643/image.png" alt="">
제가 입력한 10바이트 만큼인것을 알 수 있습니다.
즉, CMP에서 비교하는 것은 파일의 크기와 12(10진수 18) 인 것을 알 수 있습니다.
그리고 JNZ에서 분기되지 않으려면 ZF가 1이어야 하므로 CMP에서 EAX와 12는 같은 값이어야합니다.
그러므로 파일의 크기를 총 18바이트로 맞추겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/a42c2b70-3174-45d2-ac2d-d49660d41614/image.png" alt="">
이제 다시 디버깅해보면
<img src="https://velog.velcdn.com/images/sot_sky/post/8fb39a5d-f21d-414a-a377-4d95c6b5fe02/image.png" alt="">
분기점을 지나서 최종 성공 메세지 함수로 왔습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/ef308027-8919-4207-b21e-096b5d2e0f23/image.png" alt="">
성공 메세지</p>
<h1 id="2-새롭게-배운-것">2. 새롭게 배운 것</h1>
<h3 id="2-1-함수-호출-규약">2-1) 함수 호출 규약</h3>
<p>함수 호출 시 호출 전에 인자들을 스택이나 레지스터에 집어 넣은 후에 call을 수행</p>
<p>cdecl 방식
=&gt; 인자를 오른쪽에서 부터 왼쪽 순으로 스택에 넣고 스택 프레임 정리 시 호출자가 피호출자의 스택 프레임 정리 (ADD ESP, 8)
stdcall 방식
=&gt; 인자를 오른쪽에서부터 왼쪽 순으로 스택에 넣고 스택 프레임 정리 시 피호출자가 스스로 자신의 스택 프레임 정리 (RETN)
fastcall 방식
=&gt; 인자를 레지스터에 넣어 사용하고 함수 호출하고 레지스터에 값 사용 시 스택에 먼저 복사한 후에 사용</p>
<h3 id="2-2-함수">2-2) 함수</h3>
<p>CreateFileA() : 파일 또는 I/O 디바이스를 만들거나 열어줍니다.</p>
<pre><code>HANDLE CreateFileA(
  [in]           LPCSTR                lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);</code></pre><p>GetFileSize() : 지정된 파일의 크기를 검색합니다.</p>
<pre><code>DWORD GetFileSize(
  [in]            HANDLE  hFile,
  [out, optional] LPDWORD lpFileSizeHigh
);</code></pre><h3 id="2-3-명령어">2-3) 명령어</h3>
<p>CMP 인자1, 인자2
인자1 &gt; 인자2인 경우 : ZF = 0, CF = 0
인자1 = 인자2인 경우 : ZF = 1, CF = 0
인자1 &lt; 인자2인 경우 : ZF = 0, CF = 1
JE (JUMP IF EQUAL) : ZF == 0 인 경우 분기
JNZ (JUMP IF NOT EQUAL) : 같지 않으면 (즉, ZF가 0이라면) 분기</p>
<h3 id="2-4-dword-ptr-ssds-차이">2-4) DWORD PTR SS,DS 차이</h3>
<p>리버싱을 하면서 SS와 DS의 차이가 계속 궁금해서 찾아보았는데
SS는 스택영역을 나타내는 스택 세그먼트이고 DS는 데이터영역을 나타내는 데이터 세그먼트이다.
스택 세그먼트는 스택 메모리 영역의 시작 주소를 가리킵니다.
데이터 세그먼트는 전역 변수와 정적 데이터를 저장하는 메모리 영역의 시작 주소를 가리킵니다.
스택은 일시적인 연산과 저장을 위한것이고 데이터 세그먼트는 프로그램 내의 전체적인 데이터를 저장하는것으로 보면 될 것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리버싱]]></title>
            <link>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-ct6py3yo</link>
            <guid>https://velog.io/@sot_sky/%EB%A6%AC%EB%B2%84%EC%8B%B1-ct6py3yo</guid>
            <pubDate>Tue, 09 May 2023 15:41:08 GMT</pubDate>
            <description><![CDATA[<h1 id="1-abex-crackme---2">1. abex crackme - 2</h1>
<h3 id="1-1-프로그램-실행">1-1) 프로그램 실행</h3>
<p>해당 프로그램을 실행하면 name과 serial번호를 적는 목록이 나오고 check, about, quit라는 버튼이 있습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f3639fd8-34cd-4a60-8a82-6564286e8db6/image.png" alt="">
이름과 시리얼 번호를 적고 체크를 누르면 성공 시 성공 메세지와 실패 시 실패 메세지가 나타납니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/d6176ac3-9b5e-4834-b576-1f88ced49b89/image.png" alt="">
실패 메세지
<img src="https://velog.velcdn.com/images/sot_sky/post/374fbcde-fd6e-4624-80c4-5f88a70aa306/image.png" alt="">
또한 이름이 4글자 이하 시 오류 메세지가 나타납니다.</p>
<h3 id="1-2-엔트리-포인트-찾기">1-2) 엔트리 포인트 찾기</h3>
<p>마찬가지로 PEview로 Image Base와 Address of Entry Point를 확인해 보았습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/f5692492-816d-4215-a626-cc9a5c31d0a1/image.png" alt="">
Image Base(VA) 값과 Address of Entry Point(RVA) 값의 합인 00401238이 엔트리 포인트겠네요.
올리 디버거로 파일을 열자 해당위치에서 브레이크 포인트가 걸립니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/bac92bc1-c7a0-4d8d-b487-9876b039e577/image.png" alt=""></p>
<h3 id="1-3-문자열-찾기">1-3) 문자열 찾기</h3>
<p>이제 앞서 보았던 오류 메세지와 실패 메세지를 이용하여 프로그램안에 해당 메세지의 위치들을 찾아 보겠습니다.
코드에서 우클릭 후 search for에서 all referenced text string 클릭 후 문자열 창이 뜨면 우클릭 후 search for text를 클릭하면 문자열 입력 칸이 나오는데 그 곳에 보았던 4글자 이하 시 오류 메세지를 넣어보겠습니다
<img src="https://velog.velcdn.com/images/sot_sky/post/fb49c1d6-4daf-49a1-8237-ad07e569eaa1/image.png" alt="">
찾았습니다. 주변에 실패 메세지와 성공 메세지도 있네요.
더블클릭 후 해당 코드로 이동합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/b5859b53-94ab-4162-9277-b7ab4a30cbea/image.png" alt="">
내용은 0040237C 의 내용을 스택에 복사하라는 명령입니다. 그러므로 0040237C에는 Please <del>~</del>의 문구가 저장되어 있다는 것을 알 수 있습니다.</p>
<h3 id="1-4-코드-수정">1-4) 코드 수정</h3>
<p><img src="https://velog.velcdn.com/images/sot_sky/post/a2b3fbdd-70af-4604-a1de-481907a4ccba/image.png" alt="">
오류 메세지가 뜨기 직전에 브레이크 포인트를 걸고 이름 항목에 4글자 이하를 넣고 체크를 해보겠습니다.
브레이크 포인트에 걸려서 오류 메세지가 나타나기 전에 코드가 Paused 상태가 됩니다.
그 후, 스텝 오버로 Error 메세지를 스택에 복사하기전까지 실행을 해줍니다.
이 때, 힌트 영역을 보면 명령어와 관련된 <strong>레지스터, 스택, 메모리에 저장된 데이터</strong>를 보여줍니다.
그 밑에 메모리 창에 Address는 메모리 주소, Hex dump는 메모리에 저장된 데이터, Ascii는 저장된 데이터를 ASCII 코드 값으로 변경해서 나타내줍니다.
코드 라인 클릭 시 힌트 영역에 사용하는 스택의 주소가 나타납니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/9bfbaa85-95ea-41b5-89cb-c23e9eb5704f/image.png" alt="">
코드 영역에서 메모리 영역으로 이동하려면 명령어 우클릭후 Folllow in Dump에서 Immediate Constant를 클리하면 됩니다. Immediate Constant란 메모리에서 저장된 데이터를 직접 보여줍니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/35a18cfb-5130-49ab-bf37-dbcb55f16f6a/image.png" alt="">
이제 해당 명령어의 메모리 영역으로 왔습니다.
여기서 변경하고 싶은 부분의 메모리를 선택한 후 우클릭 후 edit -&gt; binary edit를 눌러 원하는 대로 데이터를 변경해주면 됩니다. 단 16진수의 값으로 넣어주어야 합니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/5503dea5-7ee0-4ef6-9a3a-52b5f7be6448/image.png" alt="">
Error!값은 밑에 right!값으로 변경해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/898235db-c2ee-4f0d-b5da-ffab03a825d4/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/1147ca77-354a-480f-8cde-eec990a0f19a/image.png" alt="">
오류 창의 타이틀이 right!로 바뀌었습니다.</p>
<h3 id="1-5-문제-해결">1-5) 문제 해결</h3>
<p>이번에는 올바르게 입력 값을 넣어 보고 프로그램을 분석 해보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/293da92e-31bd-4dd6-9e08-3722712afb44/image.png" alt="">
찾은 코드에서 조금 위로 가면 해당 부분에서 참과 거짓을 결정하고 성공과 실패 메세지를 출력하는 분기입니다.</p>
<p>TEST 명령어는 두 값은 AND연산하여 결과를 따로 저장하지 않고 제로 플래그의 값을 변경해줍니다.
만약 연산 값이 0이면 제로플래그 값은 1이되고 연산값이 0이 아니라면 제로플래그를 클리어 합니다.
주로 조건 분기문 (JE, JNE)에서 사용됩니다.
역시 바로 뒤에 JE 분기문이 있습니다. 
JE 명령어는 제로플래그가 1인 경우 지정 주소로 점프합니다.
그렇다면 이 실패를 호출한 분기문 또한 위쪽에 있을 것입니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/9756e8cd-6155-4eee-a197-83f9566f68fc/image.png" alt="">
역시 조금 올라가보니 아래에 성공 메세지를 건너뛰고 실패 메세지로 가는 분기문을 찾았습니다.
그래서 이름과 일련번호에 각각 1234, 1234를 넣고 브레이크 포인트까지 실행 시
<img src="https://velog.velcdn.com/images/sot_sky/post/afd18559-9f85-4dd9-b753-58fac66d520b/image.png" alt="">
<img src="https://velog.velcdn.com/images/sot_sky/post/f2804160-bd00-4b75-b735-22433c26e925/image.png" alt="">
제가 입력한 이름(1234)과 일련번호가 들어가있는데 일련번호는 1234가 아닌 95969798로 바뀌어 있는것을 보실 수 있습니다. 다른 함수 어딘가에서 변경된 것입니다.
그러므로 이제 프로그램에서 일련번호에 95969798을 넣어보겠습니다.
<img src="https://velog.velcdn.com/images/sot_sky/post/fbda3b4f-d3aa-47a3-8101-9350daac6e52/image.png" alt="">
성공 메세지가 나왔습니다.</p>
]]></description>
        </item>
    </channel>
</rss>