Files
2025-01-07 18:29:36 +01:00

289 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>17. Internals &#8212; PythonForWindows 1.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=fa44fd50" />
<link rel="stylesheet" type="text/css" href="_static/classic.css?v=def86cc0" />
<link rel="stylesheet" type="text/css" href="_static/css/mbasic.css?v=957880af" />
<script src="_static/documentation_options.js?v=baaebd52"></script>
<script src="_static/doctools.js?v=9a2dae69"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="18. Samples of code" href="sample.html" />
<link rel="prev" title="16. Early Work In Progress" href="wip.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="sample.html" title="18. Samples of code"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="wip.html" title="16. Early Work In Progress"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">PythonForWindows 1.0.3 documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">17. </span>Internals</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="internals">
<h1><span class="section-number">17. </span>Internals<a class="headerlink" href="#internals" title="Link to this heading"></a></h1>
<p>Because some horrible hacks of <code class="docutils literal notranslate"><span class="pre">PythonForWindows</span></code> are hidden and I wanted to talk about it.</p>
<section id="module-windows.remotectypes">
<span id="remotectypes-py"></span><h2><span class="section-number">17.1. </span>remotectypes.py<a class="headerlink" href="#module-windows.remotectypes" title="Link to this heading"></a></h2>
<p>Performing parsing of PEB / PE in remote process may be painful and i didnt want
to have two versions of all my parsing code.</p>
<p>So I made a wrapper around <code class="xref py py-mod docutils literal notranslate"><span class="pre">ctypes</span></code> that is able to do two things:</p>
<blockquote>
<div><ul class="simple">
<li><p>Transform a 32bits ctypes structure into a 64bits one and reverse</p></li>
</ul>
<p>This is done by replacing the <code class="docutils literal notranslate"><span class="pre">c_void_p</span></code>/<code class="docutils literal notranslate"><span class="pre">c_char_p</span></code> by <code class="docutils literal notranslate"><span class="pre">DWORD</span></code> or
<code class="docutils literal notranslate"><span class="pre">QWORD</span></code> and rewriting a wrapper around the <code class="xref py py-mod docutils literal notranslate"><span class="pre">ctypes</span></code> <code class="docutils literal notranslate"><span class="pre">POINTER</span></code> and other stuff.
It might not works for every structure by i didnt have any problem for now.</p>
<ul class="simple">
<li><p>Read the memory in another process</p></li>
</ul>
<p>For this one I rewrote a class that use the standard <code class="xref py py-mod docutils literal notranslate"><span class="pre">ctypes</span></code> structure
offset-size calculation, extracts those information when asked for a field and read it from the target process.
We just need to take care of special cases: <code class="docutils literal notranslate"><span class="pre">POINTER</span></code> / <code class="docutils literal notranslate"><span class="pre">ARRAY</span></code> / <code class="docutils literal notranslate"><span class="pre">STRING</span></code> / ..</p>
</div></blockquote>
<p>We also need to be carreful about the inheritance, we need to inherit from “hidden”
<code class="xref py py-class docutils literal notranslate"><span class="pre">ctypes</span></code> classes to keep the magic working.</p>
<p>This module exports the following API:</p>
<dl class="py function">
<dt class="sig sig-object py" id="windows.remotectypes.transform_type_to_remote32bits">
<span class="sig-prename descclassname"><span class="pre">windows.remotectypes.</span></span><span class="sig-name descname"><span class="pre">transform_type_to_remote32bits</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ftype</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/remotectypes.html#transform_type_to_remote32bits"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.remotectypes.transform_type_to_remote32bits" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.remotectypes.transform_type_to_remote64bits">
<span class="sig-prename descclassname"><span class="pre">windows.remotectypes.</span></span><span class="sig-name descname"><span class="pre">transform_type_to_remote64bits</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ftype</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/remotectypes.html#transform_type_to_remote64bits"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.remotectypes.transform_type_to_remote64bits" title="Link to this definition"></a></dt>
<dd></dd></dl>
<p>Both functions return a class that represent the structure in a remote process.
The class.__init__ accept two arguments:</p>
<blockquote>
<div><ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">base_addr</span></code>: the address of the object in the remote process</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code>: an object with a method <code class="docutils literal notranslate"><span class="pre">read_memory</span></code> (so a <code class="xref py py-class docutils literal notranslate"><span class="pre">windows.winobject.WinProcess</span></code> in our case)</p></li>
</ul>
</div></blockquote>
<p>Example <code class="docutils literal notranslate"><span class="pre">WinProcess.peb</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">peb</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="n">windows</span><span class="o">.</span><span class="n">current_process</span><span class="o">.</span><span class="n">bitness</span> <span class="o">==</span> <span class="mi">32</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">bitness</span> <span class="o">==</span> <span class="mi">64</span><span class="p">:</span>
<span class="k">return</span> <span class="n">RemotePEB64</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">peb_addr</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span>
<span class="k">if</span> <span class="n">windows</span><span class="o">.</span><span class="n">current_process</span><span class="o">.</span><span class="n">bitness</span> <span class="o">==</span> <span class="mi">64</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">bitness</span> <span class="o">==</span> <span class="mi">32</span><span class="p">:</span>
<span class="k">return</span> <span class="n">RemotePEB32</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">peb_addr</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span>
<span class="k">return</span> <span class="n">RemotePEB</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">peb_addr</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span>
</pre></div>
</div>
<p>I am pretty sure that this code does NOT handle all the cases, so it might break some day.</p>
</section>
<section id="module-windows.syswow64">
<span id="syswow64-py-crossing-the-heaven-gate"></span><h2><span class="section-number">17.2. </span>syswow64.py Crossing the heaven gate<a class="headerlink" href="#module-windows.syswow64" title="Link to this heading"></a></h2>
<p>One of my goal with <code class="docutils literal notranslate"><span class="pre">PythonForWindows</span></code> is to have some abstraction of the bitness of the processes.
It means being able to work on a <code class="docutils literal notranslate"><span class="pre">32bits</span> <span class="pre">Python</span></code> or a <code class="docutils literal notranslate"><span class="pre">64bits</span> <span class="pre">Python</span></code>.</p>
<p>In the case of a 32bits python on a <code class="docutils literal notranslate"><span class="pre">64bits</span></code> system (<code class="docutils literal notranslate"><span class="pre">SysWow64</span></code>) its not trivial to perform operation on
other <code class="docutils literal notranslate"><span class="pre">64bits</span></code> processes. For example directly calling <code class="xref py py-func docutils literal notranslate"><span class="pre">CreateRemoteThread()</span></code> will not work.</p>
<p>To be able to perform those operation we must be able to execute code in the <code class="docutils literal notranslate"><span class="pre">64bits</span></code> part of our
<code class="docutils literal notranslate"><span class="pre">SysWow64</span></code> process.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>See <a class="reference external" href="http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/">Knockin on Heavens Gate Dynamic Processor Mode Switching</a></p>
</div>
<p>For that we need to jump to the 64bits segment of our process, execute some code then return.
To do so, we need to use some <code class="docutils literal notranslate"><span class="pre">far</span> <span class="pre">jump</span></code> / <code class="docutils literal notranslate"><span class="pre">far</span> <span class="pre">ret</span></code> with the segments selector <code class="docutils literal notranslate"><span class="pre">0x23</span></code> (CS_32bits) and <code class="docutils literal notranslate"><span class="pre">0x33</span></code> (CS_64bits).</p>
<p>The generation of this is quite ugly in my case.
This code is in:</p>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.execute_64bits_code_from_syswow">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">execute_64bits_code_from_syswow</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#execute_64bits_code_from_syswow"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.execute_64bits_code_from_syswow" title="Link to this definition"></a></dt>
<dd></dd></dl>
<p>Once we are able to execute some code in the <code class="docutils literal notranslate"><span class="pre">64bits</span></code> part we need to create the code that will call our API (in NTDLL).
To do that, I rely on the type information already present in the function of <a class="reference internal" href="winproxy.html#module-windows.winproxy" title="windows.winproxy"><code class="xref py py-mod docutils literal notranslate"><span class="pre">windows.winproxy</span></code></a>.
With these information we are able to know</p>
<blockquote>
<div><ul class="simple">
<li><p>The name of the API</p></li>
<li><p>The number of arguments</p></li>
</ul>
</div></blockquote>
<p>Then I generate the correct x64 stub (using <a class="reference internal" href="native_exec.html#module-windows.native_exec.simple_x64" title="windows.native_exec.simple_x64"><code class="xref py py-mod docutils literal notranslate"><span class="pre">windows.native_exec.simple_x64</span></code></a>) with the function:</p>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.generate_syswow64_call">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">generate_syswow64_call</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#generate_syswow64_call"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.generate_syswow64_call" title="Link to this definition"></a></dt>
<dd></dd></dl>
<p>One problem I encountered is that our function must be able to pass values of 64bits, so passing arguments by register is not possible.</p>
<p>For now I allocate a buffer where a python wrapper copy the parameters and the x64 stub retrieves them from here.</p>
<p>(It might be possible to do something by creating a WINCFUNC with only ULONG64 parameters).</p>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.try_generate_stub_target">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">try_generate_stub_target</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#try_generate_stub_target"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.try_generate_stub_target" title="Link to this definition"></a></dt>
<dd></dd></dl>
<p>The final result is a <code class="docutils literal notranslate"><span class="pre">Python</span></code> function like the one in <a class="reference internal" href="winproxy.html#module-windows.winproxy" title="windows.winproxy"><code class="xref py py-mod docutils literal notranslate"><span class="pre">windows.winproxy</span></code></a></p>
<blockquote>
<div><ul class="simple">
<li><p>It copies the arguments in the buffer</p></li>
<li><p>Jumps on the 32-&gt;64 stub</p></li>
<li><p>X64 bits code retrieves the arguments in the buffer and setup the registers and the stack for the call</p></li>
<li><p>Calls the API</p></li>
<li><p>Returns to 32bits mode.</p></li>
</ul>
</div></blockquote>
<dl class="py class">
<dt class="sig sig-object py" id="windows.syswow64.Syswow64ApiProxy">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">Syswow64ApiProxy</span></span><a class="reference internal" href="_modules/windows/syswow64.html#Syswow64ApiProxy"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.Syswow64ApiProxy" title="Link to this definition"></a></dt>
<dd></dd></dl>
<p>Existing function are:</p>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.NtCreateThreadEx_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">NtCreateThreadEx_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#NtCreateThreadEx_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.NtCreateThreadEx_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.NtQueryInformationProcess_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">NtQueryInformationProcess_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#NtQueryInformationProcess_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.NtQueryInformationProcess_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.NtQueryInformationThread_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">NtQueryInformationThread_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#NtQueryInformationThread_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.NtQueryInformationThread_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.NtQueryVirtualMemory_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">NtQueryVirtualMemory_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#NtQueryVirtualMemory_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.NtQueryVirtualMemory_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.NtGetContextThread_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">NtGetContextThread_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#NtGetContextThread_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.NtGetContextThread_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.NtSetContextThread_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">NtSetContextThread_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#NtSetContextThread_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.NtSetContextThread_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="windows.syswow64.LdrLoadDll_32_to_64">
<span class="sig-prename descclassname"><span class="pre">windows.syswow64.</span></span><span class="sig-name descname"><span class="pre">LdrLoadDll_32_to_64</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/windows/syswow64.html#LdrLoadDll_32_to_64"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#windows.syswow64.LdrLoadDll_32_to_64" title="Link to this definition"></a></dt>
<dd></dd></dl>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<div>
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">17. Internals</a><ul>
<li><a class="reference internal" href="#module-windows.remotectypes">17.1. remotectypes.py</a><ul>
<li><a class="reference internal" href="#windows.remotectypes.transform_type_to_remote32bits"><code class="docutils literal notranslate"><span class="pre">transform_type_to_remote32bits()</span></code></a></li>
<li><a class="reference internal" href="#windows.remotectypes.transform_type_to_remote64bits"><code class="docutils literal notranslate"><span class="pre">transform_type_to_remote64bits()</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#module-windows.syswow64">17.2. syswow64.py Crossing the heaven gate</a><ul>
<li><a class="reference internal" href="#windows.syswow64.execute_64bits_code_from_syswow"><code class="docutils literal notranslate"><span class="pre">execute_64bits_code_from_syswow()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.generate_syswow64_call"><code class="docutils literal notranslate"><span class="pre">generate_syswow64_call()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.try_generate_stub_target"><code class="docutils literal notranslate"><span class="pre">try_generate_stub_target()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.Syswow64ApiProxy"><code class="docutils literal notranslate"><span class="pre">Syswow64ApiProxy</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.NtCreateThreadEx_32_to_64"><code class="docutils literal notranslate"><span class="pre">NtCreateThreadEx_32_to_64()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.NtQueryInformationProcess_32_to_64"><code class="docutils literal notranslate"><span class="pre">NtQueryInformationProcess_32_to_64()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.NtQueryInformationThread_32_to_64"><code class="docutils literal notranslate"><span class="pre">NtQueryInformationThread_32_to_64()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.NtQueryVirtualMemory_32_to_64"><code class="docutils literal notranslate"><span class="pre">NtQueryVirtualMemory_32_to_64()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.NtGetContextThread_32_to_64"><code class="docutils literal notranslate"><span class="pre">NtGetContextThread_32_to_64()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.NtSetContextThread_32_to_64"><code class="docutils literal notranslate"><span class="pre">NtSetContextThread_32_to_64()</span></code></a></li>
<li><a class="reference internal" href="#windows.syswow64.LdrLoadDll_32_to_64"><code class="docutils literal notranslate"><span class="pre">LdrLoadDll_32_to_64()</span></code></a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div>
<h4>Previous topic</h4>
<p class="topless"><a href="wip.html"
title="previous chapter"><span class="section-number">16. </span>Early Work In Progress</a></p>
</div>
<div>
<h4>Next topic</h4>
<p class="topless"><a href="sample.html"
title="next chapter"><span class="section-number">18. </span>Samples of code</a></p>
</div>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/internals.rst.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="sample.html" title="18. Samples of code"
>next</a> |</li>
<li class="right" >
<a href="wip.html" title="16. Early Work In Progress"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">PythonForWindows 1.0.3 documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">17. </span>Internals</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2015-2020, Clement Rouault.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 7.3.7.
</div>
</body>
</html>