<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>idapython &#8211; PT ESC</title>
	<atom:link href="/tag/idapython/feed/index.xml" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description></description>
	<lastBuildDate>Wed, 30 Sep 2026 19:21:10 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>/wp-content/uploads/2026/09/cropped-cropped-large_icon-32x32.png</url>
	<title>idapython &#8211; PT ESC</title>
	<link>/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Как починить CFG. Часть вторая</title>
		<link>/how-to-fix-cfg-part-two/</link>
		
		<dc:creator><![CDATA[global_author]]></dc:creator>
		<pubDate>Mon, 24 Mar 2025 17:59:29 +0000</pubDate>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[idapython]]></category>
		<category><![CDATA[reverse]]></category>
		<category><![CDATA[tips]]></category>
		<guid isPermaLink="false">http://localhost:8080/%d0%ba%d0%b0%d0%ba-%d0%bf%d0%be%d1%87%d0%b8%d0%bd%d0%b8%d1%82%d1%8c-cfg-%d1%87%d0%b0%d1%81%d1%82%d1%8c-%d0%b2%d1%82%d0%be%d1%80%d0%b0%d1%8f/</guid>

					<description><![CDATA[Как починить CFG. Часть вторая 🛠 Ранее мы рассказывали про то, как восстановить Control Flow Graph (CFG) в случае его обфускации. Однако часто при анализе даже необфусцированного ВПО можно встретить случаи, когда…]]></description>
										<content:encoded><![CDATA[<strong>Как починить CFG. Часть вторая </strong>🛠<br />
<br />
Ранее <a href="https://t.me/ptescalator/134" rel="noopener" target="_blank">мы рассказывали</a> про то, как восстановить Control Flow Graph (CFG) в случае его обфускации. Однако часто при анализе даже необфусцированного ВПО можно встретить случаи, когда CFG некоторых функций генерируется с ошибками. Одним из таких примеров является написанное на Delphi ВПО, где из-за особенностей обработки исключений часто можно встретить картину, <em>как на скриншоте 1</em>.<br />
<br />
🤔 Если присмотреться внимательнее (<em>скриншот 2</em>), то можно заметить, что в блоке (1) на стек сохраняется адрес одного из последующих блоков (3), после чего выполняется некоторая логика (зачастую — освобождение ресурсов или объектов) и происходит прыжок на сохраненный ранее адрес (2).<br />
<br />
Из-за того что в блоке 2 присутствует дополнительная ссылка, IDA не может однозначно определить адрес, куда будет осуществлен прыжок в <code>jmp eax</code>. Чтобы исправить эту проблему, напишем небольшой хук, который будет проверять и автоматически патчить подобные места в коде.<br />
<br />
Создадим класс хука, который будет ожидать событие <code>ev_ana_insn</code>. Для начала необходимо убедиться, что это действительно интересующая нас последовательность, после чего пройтись «вверх» и попытаться найти сохраненный на стек адрес. Затем пропатчить <code>jmp eax</code> на <code>jmp short address</code>.<br />
<br />
<pre class="wp-block-code"><span><code lang="x86asm" class="hljs language-x86asm language-x86asm">class DelphiJmpEaxFixer(idaapi<span class="hljs-number">.</span>IDP_Hooks):
    def lookup_push_insn(self, start: <span class="hljs-keyword">int</span>, limit: <span class="hljs-keyword">int</span> = <span class="hljs-number">30</span>) -&gt; <span class="hljs-keyword">int</span> | None:
        ...
 
    def ev_ana_insn(self, insn: idaapi<span class="hljs-number">.</span>insn_t) -&gt; bool:
        #
        b = bytes(idaapi<span class="hljs-number">.</span>get_bytes(insn<span class="hljs-number">.</span>ea - <span class="hljs-number">1</span>, <span class="hljs-number">3</span>))
        if idaapi<span class="hljs-number">.</span>is_tail(idaapi<span class="hljs-number">.</span>get_flags(insn<span class="hljs-number">.</span>ea)):
            return True
        # <span class="hljs-keyword">pop</span> <span class="hljs-built_in">eax</span> | <span class="hljs-number">58</span>
        # <span class="hljs-keyword">jmp</span> <span class="hljs-built_in">eax</span> | ff e0
        # ensure all <span class="hljs-keyword">pop</span> &amp; <span class="hljs-keyword">jmp</span> seq
        if b&#91;<span class="hljs-number">0</span>] != <span class="hljs-number">0x58</span> <span class="hljs-keyword">or</span> b&#91;<span class="hljs-number">1</span>] != <span class="hljs-number">0xFF</span> <span class="hljs-keyword">or</span> b&#91;<span class="hljs-number">2</span>] != <span class="hljs-number">0xE0</span>:
            return False
 
        print(f<span class="hljs-string">"Got jmp short eax at {insn.ea:x}"</span>)
        pushed_address = self<span class="hljs-number">.</span>lookup_push_insn(insn<span class="hljs-number">.</span>ea)
        if pushed_address is None:
            return False
        delta = pushed_address - insn<span class="hljs-number">.</span>ea
        if delta &lt; <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> delta &gt; <span class="hljs-number">128</span>:
            return False
        print(f<span class="hljs-string">"{delta=}"</span>)
 
        asm_call = f<span class="hljs-string">"jmp short {delta}"</span>
        assembled = idaapi<span class="hljs-number">.</span>AssembleLine(insn<span class="hljs-number">.</span>ea, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, True, asm_call)
        if assembled is None:
            return False
        return idaapi<span class="hljs-number">.</span>patch_bytes(insn<span class="hljs-number">.</span>ea, assembled)
</code></span></pre><br />
<br />
Напишем функцию поиска сохраняемого на стек адреса <code>lookup_push_insn</code>. В ней найдем предположительную верхнюю границу блока 2 и проверим, что блок имеет единственную ссылку. Дополнительно ограничим диапазон поиска, в целях оптимизации.<br />
<br />
<pre class="wp-block-code"><span><code lang="python" class="hljs language-python language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lookup_push_insn</span><span class="hljs-params">(self, start: int, limit: int = <span class="hljs-number">30</span>)</span> -&gt; int | <span class="hljs-keyword">None</span>:</span>
    ptr: int = start
    insn = idaapi.insn_t()
    jmp_ref = idaapi.BADADDR
    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(limit, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>):
        prev_addr = idaapi.decode_prev_insn(insn, ptr)
        <span class="hljs-keyword">if</span> prev_addr == idaapi.BADADDR:
            <span class="hljs-keyword">break</span>
        ptr = prev_addr
        _refs = &#91;xref <span class="hljs-keyword">for</span> xref <span class="hljs-keyword">in</span> idautils.CodeRefsTo(ptr, <span class="hljs-literal">False</span>)]
        <span class="hljs-comment"># If we found refs it's likely an upper basic block address</span>
        <span class="hljs-comment"># it must be a single jmp ref</span>
        <span class="hljs-keyword">if</span> _refs:
            <span class="hljs-keyword">if</span> len(_refs) != <span class="hljs-number">1</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
            jmp_ref = next(iter(_refs))
            <span class="hljs-keyword">break</span>
 
    <span class="hljs-keyword">if</span> jmp_ref == idaapi.BADADDR:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
 
    ref_insn_sz = idaapi.decode_insn(insn, jmp_ref)
    <span class="hljs-keyword">if</span> ref_insn_sz == <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> insn.itype != idaapi.NN_jmp:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
 
    addr = idaapi.decode_prev_insn(insn, ptr)
    <span class="hljs-keyword">if</span> addr == idaapi.BADADDR <span class="hljs-keyword">or</span> insn.itype != idaapi.NN_push:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
 
    <span class="hljs-keyword">return</span> insn.Op1.value
</code></span></pre><br />
<br />
Добавим инициализацию хука при запуске скрипта и загрузим его в IDA. Запустим повторный анализ бинарного файла. В результате получим исправленный граф (<em>скриншот 3</em>).<br />
<br />
Оставшиеся одиночные блоки — это вызовы обработчиков исключений; в данном случае они не влияют на ход работы программы. Стоит помнить, что пока хук активен, он будет автоматически вызываться даже при разметке нового кода, который не был размечен ранее.<br />
<br />
<pre class="wp-block-code"><span><code lang="plaintext" class="hljs language-plaintext language-plaintext">hook_instance = DelphiJmpEaxFixer()
hook_instance.hook()
</code></span></pre><br />
<br />
<strong>Happy reversing!</strong> 💫

<figure class="wp-block-image"><img width="748" height="1024" src="/wp-content/uploads/2025/03/photo_248@24-03-2025_17-59-30-748x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/03/photo_248@24-03-2025_17-59-30-748x1024.jpg 748w, /wp-content/uploads/2025/03/photo_248@24-03-2025_17-59-30-219x300.jpg 219w, /wp-content/uploads/2025/03/photo_248@24-03-2025_17-59-30-768x1051.jpg 768w, /wp-content/uploads/2025/03/photo_248@24-03-2025_17-59-30.jpg 935w" sizes="auto, (max-width: 748px) 100vw, 748px" /></figure>
<figure class="wp-block-image"><img width="516" height="1024" src="/wp-content/uploads/2025/03/photo_249@24-03-2025_17-59-30-516x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/03/photo_249@24-03-2025_17-59-30-516x1024.jpg 516w, /wp-content/uploads/2025/03/photo_249@24-03-2025_17-59-30-151x300.jpg 151w, /wp-content/uploads/2025/03/photo_249@24-03-2025_17-59-30.jpg 645w" sizes="auto, (max-width: 516px) 100vw, 516px" /></figure>

<br />
<br />
<span class="hashtag">#tip</span> <span class="hashtag">#reverse</span> <span class="hashtag">#idapython</span><br />
<a href="https://t.me/ptescalator" rel="noopener" target="_blank">@ptescalator</a>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Статический резолв импортов</title>
		<link>/static-import-resolution/</link>
		
		<dc:creator><![CDATA[global_author]]></dc:creator>
		<pubDate>Thu, 23 Jan 2025 10:45:34 +0000</pubDate>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[idapython]]></category>
		<category><![CDATA[reverse]]></category>
		<category><![CDATA[tips]]></category>
		<guid isPermaLink="false">http://localhost:8080/%d1%81%d1%82%d0%b0%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b9-%d1%80%d0%b5%d0%b7%d0%be%d0%bb%d0%b2-%d0%b8%d0%bc%d0%bf%d0%be%d1%80%d1%82%d0%be%d0%b2/</guid>

					<description><![CDATA[Статический резолв импортов 👨‍💻 Динамический резолв импортов по хеш-суммам в ВПО — тема заезженная, но для проведения статического анализа необходимо разметить имена и прототипы API. Чтобы из того, что представлено на скрине…]]></description>
										<content:encoded><![CDATA[<strong>Статический резолв импортов</strong> 👨‍💻<br />
<br />
Динамический резолв импортов по хеш-суммам в ВПО — тема заезженная, но для проведения статического анализа необходимо разметить имена и прототипы API. Чтобы из того, что представлено <em>на скрине 1</em>, получить то, что <em>на скрине 2</em>, и не мучаться с ручной разметкой, можно написать скрипт IDAPython.<br />
<br />
😠 На примере <a href="https://www.zscaler.com/blogs/security-research/dodgebox-deep-dive-updated-arsenal-apt41-part-1" rel="noopener" target="_blank">DodgeBox</a> рассмотрим резолв, который заключается в вычислении адреса API-функции и помещении его в глобальную структуру. Реализация — <em>на скрине 3</em>.<br />
<br />
Алгоритм хеширования опустим, поскольку здесь он не столь важен. Перед началом необходимо подготовить словарь с именами функций WinAPI и их хеш-суммами. Для этого выберем те библиотеки, что используются в бинаре. Здесь есть имена DLL в открытом виде, но иногда — только хеш-суммы, в этом случае можно составить словарь из всех системных DLL. Наш словарь — <em>на скрине 4</em>.<br />
<br />
Далее убеждаемся, что члены в структуре заполняются последовательно, чтобы автоматически извлечь из кода хеш-суммы, имена модулей. Внимание — первый член структуры пропускается. Создаем структуру нужного размера (<em>скрин 5</em>), чтобы она вместила все функции.<br />
<br />
Чтобы извлечь хеш-суммы и имена библиотек, пишем функцию, которая пройдет по всем вызовам <code>get_proc_by_hash</code> и извлечет ее аргументы.<br />
<br />
<pre class="wp-block-code"><span><code lang="python" class="hljs language-python language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_hashes</span><span class="hljs-params">(resolve_API_addr, get_proc_by_hash_addr)</span>:</span>
  result = &#91;]
  func: ida_funcs.func_t
  func = ida_funcs.get_func(resolve_API_addr)

  cur = func.start_ea
  <span class="hljs-keyword">while</span> cur &lt; func.end_ea:
    <span class="hljs-keyword">if</span> get_operand_value(cur, <span class="hljs-number">0</span>) == get_proc_by_hash_addr:
      result.append(get_args(cur))
    cur = ida_search.find_code(cur, SEARCH_DOWN)

  <span class="hljs-keyword">return</span> result</code></span></pre><br />
<br />
Функция <code>get_args</code> поднимается на несколько шагов вверх от операции <code>call</code> и извлекает аргументы.<br />
<br />
<pre class="wp-block-code"><span><code lang="x86asm" class="hljs language-x86asm language-x86asm">def get_args(call_addr):
  func_name_hash = None
  lib_name = None

  cur = call_addr
<span class="hljs-symbol">  True:</span>
    if print_insn_mnem(cur) == <span class="hljs-string">"mov"</span> <span class="hljs-keyword">and</span> print_operand(cur, <span class="hljs-number">0</span>) == <span class="hljs-string">"r8d"</span>:
      func_name_hash = get_operand_value(cur, <span class="hljs-number">1</span>) &amp; <span class="hljs-number">0xFFFFFFFF</span>
    elif print_insn_mnem(cur) == <span class="hljs-string">"lea"</span> <span class="hljs-keyword">and</span> print_operand(cur, <span class="hljs-number">0</span>) == <span class="hljs-string">"rcx"</span>:
      lib_name = ida_bytes<span class="hljs-number">.</span>get_strlit_contents(get_operand_value(cur, <span class="hljs-number">1</span>), -<span class="hljs-number">1</span>, STRTYPE_C_16).decode()

    if func_name_hash <span class="hljs-keyword">and</span> lib_name:
      return lib_name, func_name_hash
        
    cur = ida_search<span class="hljs-number">.</span>find_code(cur, SEARCH_UP)</code></span></pre><br />
<br />
На выходе <code>get_hashes</code> получим список, который используем для заполнения структуры API. Основная функция будет выглядеть так:<br />
<br />
<pre class="wp-block-code"><span><code lang="yaml" class="hljs language-yaml language-yaml"><span class="hljs-attr">struc:</span> <span class="hljs-string">ida_struct.struc_t</span> <span class="hljs-string">=</span> <span class="hljs-string">ida_struct.get_struc(ida_struct.get_struc_id("API"))</span>

<span class="hljs-string">funcs</span> <span class="hljs-string">=</span> <span class="hljs-string">get_hashes(0x180007A90,</span> <span class="hljs-number">0x1800078E0</span><span class="hljs-string">)</span>
<span class="hljs-string">for</span> <span class="hljs-string">i</span> <span class="hljs-string">in</span> <span class="hljs-string">range(1,</span> <span class="hljs-string">len(funcs)</span> <span class="hljs-string">+</span> <span class="hljs-number">1</span><span class="hljs-string">):</span>

  <span class="hljs-string">lib_name,</span> <span class="hljs-string">func_name_hash</span> <span class="hljs-string">=</span> <span class="hljs-string">funcs&#91;i</span> <span class="hljs-bullet">-</span> <span class="hljs-number">1</span><span class="hljs-string">]</span>
  <span class="hljs-attr">member:</span> <span class="hljs-string">ida_struct.member_t</span> <span class="hljs-string">=</span> <span class="hljs-string">struc.members&#91;i]</span>

  <span class="hljs-string">func_name</span> <span class="hljs-string">=</span> <span class="hljs-string">get_func_name(lib_name,</span> <span class="hljs-string">func_name_hash,</span> <span class="hljs-string">winapi_hashes_dict)</span>
  <span class="hljs-attr">if func_name:</span>
    <span class="hljs-string">ida_struct.set_member_name(struc,</span> <span class="hljs-string">member.soff,</span> <span class="hljs-string">func_name)</span>
  <span class="hljs-string">func_tinfo</span> <span class="hljs-string">=</span> <span class="hljs-string">get_func_tinfo(func_name)</span>
  <span class="hljs-attr">if func_tinfo:</span>
    <span class="hljs-string">ida_struct.set_member_tinfo(struc,</span> <span class="hljs-string">member,</span> <span class="hljs-number">0</span><span class="hljs-string">,</span> <span class="hljs-string">func_tinfo,</span> <span class="hljs-number">0</span><span class="hljs-string">)</span></code></span></pre><br />
<br />
Функция <code>get_func_name</code> проста в реализации, она находит в словаре имя API по хеш-сумме. А вот <code>get_func_tinfo</code> более интересна: она создает объект, содержащий прототип функции, который мы также применим к члену структуры.<br />
<br />
<pre class="wp-block-code"><span><code lang="python" class="hljs language-python language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_func_tinfo</span><span class="hljs-params">(func_name)</span>:</span>
  tinfo = ida_typeinf.get_named_type(<span class="hljs-literal">None</span>, func_name, <span class="hljs-number">0</span>)
  <span class="hljs-keyword">if</span> tinfo:
    type_s = tinfo&#91;<span class="hljs-number">1</span>]
    field_s = tinfo&#91;<span class="hljs-number">2</span>]
    t = ida_typeinf.tinfo_t()
    t.deserialize(<span class="hljs-literal">None</span>, type_s, field_s)
    t.create_ptr(t)
    <span class="hljs-keyword">return</span> t
  <span class="hljs-keyword">else</span>:
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span></code></span></pre><br />
<br />
Функция <code>ida_typeinf.get_named_type</code> получает информацию о типе, который содержится в <code>Type Library (*.til)</code>. Вызов выглядит так:<br />
<br />
<pre class="wp-block-code"><span><code lang="plaintext" class="hljs language-plaintext language-plaintext">Python&gt;get_func_tinfo("GetWindowsDirectoryW")
UINT (__stdcall *)(LPWSTR lpBuffer, UINT uSize)</code></span></pre><br />
<br />
Однако на самом деле функция возвращает объект типа <code>ida_typeinf.tinfo_t</code>.<br />
<br />
Структура API после вызова скрипта представлена <em>на скрине 6</em>. Если применить ее к глобальной переменной, резолв превратится в то, что видно <em>на скрине 7</em>, и можно будет удобно анализировать бинарь статически, не запуская отладчик.<br />
<br />
Есть, конечно, соблазн написать функцию <code>make_beautifully</code>, которая сама вычитает офсеты, создаст структуру и члены внутри нее, но об этом в другой раз.

<figure class="wp-block-image"><img width="1024" height="683" src="/wp-content/uploads/2025/01/photo_180@23-01-2025_10-45-34-1024x683.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/01/photo_180@23-01-2025_10-45-34-1024x683.jpg 1024w, /wp-content/uploads/2025/01/photo_180@23-01-2025_10-45-34-300x200.jpg 300w, /wp-content/uploads/2025/01/photo_180@23-01-2025_10-45-34-768x512.jpg 768w, /wp-content/uploads/2025/01/photo_180@23-01-2025_10-45-34.jpg 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="1024" src="/wp-content/uploads/2025/01/photo_181@23-01-2025_10-45-34-1024x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/01/photo_181@23-01-2025_10-45-34-1024x1024.jpg 1024w, /wp-content/uploads/2025/01/photo_181@23-01-2025_10-45-34-300x300.jpg 300w, /wp-content/uploads/2025/01/photo_181@23-01-2025_10-45-34-150x150.jpg 150w, /wp-content/uploads/2025/01/photo_181@23-01-2025_10-45-34-768x768.jpg 768w, /wp-content/uploads/2025/01/photo_181@23-01-2025_10-45-34.jpg 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="1024" src="/wp-content/uploads/2025/01/photo_182@23-01-2025_10-45-34-1024x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/01/photo_182@23-01-2025_10-45-34-1024x1024.jpg 1024w, /wp-content/uploads/2025/01/photo_182@23-01-2025_10-45-34-300x300.jpg 300w, /wp-content/uploads/2025/01/photo_182@23-01-2025_10-45-34-150x150.jpg 150w, /wp-content/uploads/2025/01/photo_182@23-01-2025_10-45-34-768x768.jpg 768w, /wp-content/uploads/2025/01/photo_182@23-01-2025_10-45-34.jpg 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="683" src="/wp-content/uploads/2025/01/photo_183@23-01-2025_10-45-34-1024x683.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/01/photo_183@23-01-2025_10-45-34-1024x683.jpg 1024w, /wp-content/uploads/2025/01/photo_183@23-01-2025_10-45-34-300x200.jpg 300w, /wp-content/uploads/2025/01/photo_183@23-01-2025_10-45-34-768x512.jpg 768w, /wp-content/uploads/2025/01/photo_183@23-01-2025_10-45-34.jpg 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="683" src="/wp-content/uploads/2025/01/photo_184@23-01-2025_10-45-34-1024x683.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/01/photo_184@23-01-2025_10-45-34-1024x683.jpg 1024w, /wp-content/uploads/2025/01/photo_184@23-01-2025_10-45-34-300x200.jpg 300w, /wp-content/uploads/2025/01/photo_184@23-01-2025_10-45-34-768x512.jpg 768w, /wp-content/uploads/2025/01/photo_184@23-01-2025_10-45-34.jpg 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="683" src="/wp-content/uploads/2025/01/photo_185@23-01-2025_10-45-34-1024x683.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2025/01/photo_185@23-01-2025_10-45-34-1024x683.jpg 1024w, /wp-content/uploads/2025/01/photo_185@23-01-2025_10-45-34-300x200.jpg 300w, /wp-content/uploads/2025/01/photo_185@23-01-2025_10-45-34-768x512.jpg 768w, /wp-content/uploads/2025/01/photo_185@23-01-2025_10-45-34.jpg 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>

<br />
<br />
<span class="hashtag">#tip</span> <span class="hashtag">#reverse</span> <span class="hashtag">#idapython</span><br />
<a href="https://t.me/ptescalator" rel="noopener" target="_blank">@ptescalator</a>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Как починить CFG</title>
		<link>/how-to-fix-cfg/</link>
		
		<dc:creator><![CDATA[global_author]]></dc:creator>
		<pubDate>Tue, 22 Oct 2024 17:18:28 +0000</pubDate>
				<category><![CDATA[Разное]]></category>
		<category><![CDATA[idapython]]></category>
		<category><![CDATA[reverse]]></category>
		<category><![CDATA[tips]]></category>
		<guid isPermaLink="false">http://localhost:8080/%d0%ba%d0%b0%d0%ba-%d0%bf%d0%be%d1%87%d0%b8%d0%bd%d0%b8%d1%82%d1%8c-cfg/</guid>

					<description><![CDATA[Как починить CFG 🔧 В процессе реверса ВПО мы встречаемся со случаями, когда обфускация мешает понять общий алгоритм работы. Один из примеров — создание двух последовательных противоположных условных переходов в одну точку.…]]></description>
										<content:encoded><![CDATA[<strong>Как починить CFG</strong> 🔧<br />
<br />
В процессе реверса ВПО мы встречаемся со случаями, когда обфускация мешает понять общий алгоритм работы. Один из примеров — создание двух последовательных противоположных условных переходов в одну точку.<br />
<br />
<strong>Схематично это выглядит так:</strong><br />
<br />
<pre class="wp-block-code"><span><code lang="yaml" class="hljs language-yaml language-yaml">
<span class="hljs-attr">start:</span>
    <span class="hljs-string">jnX</span> <span class="hljs-string">labelA</span>
    <span class="hljs-string">jX</span> <span class="hljs-string">labelA</span>
<span class="hljs-attr">labelA:</span>
    <span class="hljs-string">&lt;bytes&gt;</span>
</code></span></pre><br />
<br />
То есть IDA при анализе кода идет сначала по ветке <code>False</code> и создает код там, откладывая ветку <code>True</code> на потом. Встретив инструкцию <code>jnX</code>, IDA создает код сразу после текущей.<br />
<br />
Далее, встретив противоположный условный переход, она снова идет по ветке <code>False</code> и создает код на следующем адресе, построив мусорную инструкцию, после которой дизассемблировать уже нельзя. Тогда IDA возвращается к отложенной на потом очереди и берет адрес оттуда, но беда в том, что там код уже создан, а значит анализ завершается (<em>хорошо видно на скриншоте 1</em>).<br />
<br />
При живом исполнении в независимости от состояния флагов будет выполнен переход на операнд, то есть выполнение будет <em>как на скриншоте 2</em>, если мы подправим control flow.<br />
<br />
Трудность при исследовании таких шеллкодов в том, что переходов может быть много и каждый раз анализ будет о них спотыкаться. Можно попробовать починить вручную, но если таких блоков будут сотни?<br />
<br />
✍️ Чтобы справиться с этим, напишем несложный скрипт на <code>IDAPython</code>, который исправит проблему автоматически. Задача — найти эти блоки и пропатчить их.<br />
<br />
Сначала составим список противоположных условных переходов и положим его в функцию, которая будет сверять две последовательные инструкции со списком:<br />
<br />
<pre class="wp-block-code"><span><code lang="python" class="hljs language-python language-python">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">c_jumps</span><span class="hljs-params">(addr, n_addr)</span>:</span>
    ops = &#91;
        (<span class="hljs-string">"jz"</span>, <span class="hljs-string">"jnz"</span>),
        (<span class="hljs-string">"jnz"</span>, <span class="hljs-string">"jz"</span>),
        (<span class="hljs-string">"je"</span>, <span class="hljs-string">"jne"</span>),
        (<span class="hljs-string">"jne"</span>, <span class="hljs-string">"je"</span>),
        ...
    ]
    <span class="hljs-keyword">if</span> (ida_ua.print_insn_mnem(addr), ida_ua.print_insn_mnem(n_addr)) <span class="hljs-keyword">in</span> ops:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code></span></pre><br />
<br />
Будем последовательно проходить каждую инструкцию до тех пор, пока не встретим нужные либо не упремся в лимит.<br />
<br />
<pre class="wp-block-code"><span><code lang="python" class="hljs language-python language-python">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deobf</span><span class="hljs-params">(start, limit=BADADDR)</span>:</span>
    <span class="hljs-keyword">while</span> addr != BADADDR:
        n_addr = ida_search.find_code(addr, ida_search.SEARCH_DOWN)
        <span class="hljs-keyword">if</span> n_addr == BADADDR:
            <span class="hljs-keyword">break</span>
 
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> c_jumps(addr, n_addr):
            addr = n_addr
            <span class="hljs-keyword">continue</span>
</code></span></pre><br />
<br />
🧐 С помощью метода <code>find_code</code> из модуля <code>ida_search</code> находим следующий адрес, на котором есть код, а с помощью функции <code>c_jumps</code> проверяем, являются ли инструкции на этом и следующем адресе противоположными прыжками. Найдя их, мы должны проверить, указывают ли эти прыжки на одну точку (то есть равны ли их операнды):<br />
<br />
<pre class="wp-block-code"><span><code lang="plaintext" class="hljs language-plaintext language-plaintext">
o1 = get_operand_value(addr, 0)
o2 = get_operand_value(n_addr, 0)
 
if o1 != o2:
    addr = n_addr
    continue
 
insn = ida_ua.insn_t()
l1 = ida_ua.decode_insn(insn, addr)
l2 = ida_ua.decode_insn(insn, n_addr)
</code></span></pre><br />
<br />
С помощью <code>get_operand_value</code> получаем значение операндов (у jX и jXX он один) и проверяем их равенство. Чтобы определить длину отрезка, который нужно пропатчить, с помощью <code>decode_insn</code> из <code>ida_ua</code> находим длины инструкций.<br />
<br />
Затем исправляем условные переходы и просим IDA проанализировать новый код. Это необходимо, чтобы можно было найти дальнейшие блоки обфускации:<br />
<br />
<pre class="wp-block-code"><span><code lang="plaintext" class="hljs language-plaintext language-plaintext">
after_addr = n_addr + l2
ida_bytes.patch_bytes(addr, bytes(&#91;0x90] * (l1 + l2 + 1)))

ida_auto.auto_wait()
ida_bytes.del_items(after_addr, ida_bytes.DELIT_EXPAND)
ida_auto.auto_wait()

ida_ua.create_insn(o1)
addr = o1
ida_auto.auto_wait()
</code></span></pre><br />
<br />
🗂 Методом <code>patch_bytes</code>  из <code>ida_bytes</code> мы патчим инструкции, с помощью <code>auto_wait из ida_auto</code> просим IDA проанализировать новый код, затем, используя <code>del_items</code>, удаляем мусорные инструкции, созданные при первичном анализе, и снова анализируем. С помощью <code>create_insn</code> создаем валидную инструкцию там, куда указывали условные переходы, и переанализируем в последний раз.<br />
<br />
Таким образом, этот скрипт позволяет из обфусцированного кода получить нормальный ASM-код, <em>как на скриншоте 3</em>.<br />
<br />
Иногда после него остаются единичные нераспознанные байты, но их легко поправить. Из этого кода можно создать функцию, декомпилировать ее и проанализировать (<em>как на скриншоте 4</em>).<br />
<br />
Изучайте IDAPython, пишите скрипты. Happy reversing!

<figure class="wp-block-image"><img width="1024" height="1024" src="/wp-content/uploads/2024/10/photo_98@22-10-2024_17-18-28-1024x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2024/10/photo_98@22-10-2024_17-18-28-1024x1024.jpg 1024w, /wp-content/uploads/2024/10/photo_98@22-10-2024_17-18-28-300x300.jpg 300w, /wp-content/uploads/2024/10/photo_98@22-10-2024_17-18-28-150x150.jpg 150w, /wp-content/uploads/2024/10/photo_98@22-10-2024_17-18-28-768x768.jpg 768w, /wp-content/uploads/2024/10/photo_98@22-10-2024_17-18-28.jpg 1280w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="1024" src="/wp-content/uploads/2024/10/photo_99@22-10-2024_17-18-28-1024x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2024/10/photo_99@22-10-2024_17-18-28-1024x1024.jpg 1024w, /wp-content/uploads/2024/10/photo_99@22-10-2024_17-18-28-300x300.jpg 300w, /wp-content/uploads/2024/10/photo_99@22-10-2024_17-18-28-150x150.jpg 150w, /wp-content/uploads/2024/10/photo_99@22-10-2024_17-18-28-768x768.jpg 768w, /wp-content/uploads/2024/10/photo_99@22-10-2024_17-18-28.jpg 1280w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image"><img width="1024" height="1024" src="/wp-content/uploads/2024/10/photo_100@22-10-2024_17-18-28-1024x1024.jpg" class="attachment-large size-large" alt="" decoding="async" loading="lazy" srcset="/wp-content/uploads/2024/10/photo_100@22-10-2024_17-18-28-1024x1024.jpg 1024w, /wp-content/uploads/2024/10/photo_100@22-10-2024_17-18-28-300x300.jpg 300w, /wp-content/uploads/2024/10/photo_100@22-10-2024_17-18-28-150x150.jpg 150w, /wp-content/uploads/2024/10/photo_100@22-10-2024_17-18-28-768x768.jpg 768w, /wp-content/uploads/2024/10/photo_100@22-10-2024_17-18-28.jpg 1280w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>

<br />
<br />
<span class="hashtag">#tip</span> <span class="hashtag">#reverse</span> <span class="hashtag">#idapython</span><br />
<a href="https://t.me/ptescalator" rel="noopener" target="_blank">@ptescalator</a>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
