├── OllyDBG2-Python.7z ├── samples ├── layers │ ├── layers.exe │ ├── result.png │ ├── README.md │ └── layers.py ├── testing │ ├── result.png │ ├── testing.exe │ ├── README.md │ └── testing.py ├── call_stack │ ├── result.png │ ├── call_stack.exe │ ├── README.md │ └── call_stack.py ├── gdbserver │ ├── result.png │ ├── README.md │ └── gdbserver.py ├── breakpoints │ ├── result.png │ ├── breakpoints.exe │ ├── README.md │ └── breakpoints.py ├── int3_exception │ ├── result.png │ ├── int3_exception.exe │ ├── README.md │ └── int3_exception.py ├── upx_oep_finder │ ├── result.png │ ├── testing_upxed.exe │ ├── README.md │ └── upx_oep_finder.py ├── hackyou300_dumped │ ├── result.png │ ├── hackyou_re300_dumped.exe │ ├── README.md │ └── hackyou_re300_dumped.py ├── disassemble_assemble │ ├── result.png │ ├── README.md │ └── disassemble_assemble.py └── import_ida_symbols │ ├── result.png │ ├── import_ida_symbols.exe │ ├── README.md │ ├── import_ida_symbols.map │ └── import_ida_symbols.py ├── ollydbg2-plugin-development-files ├── lib │ └── ollydbg.lib ├── swig │ ├── generate_python_module.bat │ ├── ollydbg2-swig.i │ └── correct_cdecl_fpointers.py ├── ollydbg2-python-loader │ ├── window.hpp │ ├── toolbox.cpp │ ├── toolbox.hpp │ ├── python-loader.sln │ ├── main.hpp │ ├── window.cpp │ ├── main.cpp │ └── python-loader.vcxproj ├── README.md └── ollydbg2-python-bindings-swig │ ├── python-bindings-swig.sln │ ├── python-bindings-swig.vcxproj.filters │ └── python-bindings-swig.vcxproj ├── ollyapi ├── memory.py ├── __init__.py ├── threads.py ├── breakpoints.py ├── sym.py └── utils.py └── README.md /OllyDBG2-Python.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/OllyDBG2-Python.7z -------------------------------------------------------------------------------- /samples/layers/layers.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/layers/layers.exe -------------------------------------------------------------------------------- /samples/layers/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/layers/result.png -------------------------------------------------------------------------------- /samples/testing/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/testing/result.png -------------------------------------------------------------------------------- /samples/call_stack/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/call_stack/result.png -------------------------------------------------------------------------------- /samples/gdbserver/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/gdbserver/result.png -------------------------------------------------------------------------------- /samples/testing/testing.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/testing/testing.exe -------------------------------------------------------------------------------- /samples/breakpoints/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/breakpoints/result.png -------------------------------------------------------------------------------- /samples/call_stack/call_stack.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/call_stack/call_stack.exe -------------------------------------------------------------------------------- /samples/int3_exception/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/int3_exception/result.png -------------------------------------------------------------------------------- /samples/upx_oep_finder/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/upx_oep_finder/result.png -------------------------------------------------------------------------------- /samples/breakpoints/breakpoints.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/breakpoints/breakpoints.exe -------------------------------------------------------------------------------- /samples/hackyou300_dumped/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/hackyou300_dumped/result.png -------------------------------------------------------------------------------- /samples/disassemble_assemble/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/disassemble_assemble/result.png -------------------------------------------------------------------------------- /samples/import_ida_symbols/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/import_ida_symbols/result.png -------------------------------------------------------------------------------- /samples/int3_exception/int3_exception.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/int3_exception/int3_exception.exe -------------------------------------------------------------------------------- /samples/upx_oep_finder/testing_upxed.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/upx_oep_finder/testing_upxed.exe -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/lib/ollydbg.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/ollydbg2-plugin-development-files/lib/ollydbg.lib -------------------------------------------------------------------------------- /samples/import_ida_symbols/import_ida_symbols.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/import_ida_symbols/import_ida_symbols.exe -------------------------------------------------------------------------------- /samples/hackyou300_dumped/hackyou_re300_dumped.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0vercl0k/ollydbg2-python/HEAD/samples/hackyou300_dumped/hackyou_re300_dumped.exe -------------------------------------------------------------------------------- /samples/layers/README.md: -------------------------------------------------------------------------------- 1 | # Samples - layers 2 | This example shows you how you can use OllyDbg2-Python to pass a ton of layers. The program uses simple layers (337 ones). 3 | 4 | ![layers](result.png) 5 | -------------------------------------------------------------------------------- /samples/testing/README.md: -------------------------------------------------------------------------------- 1 | # Samples - testing 2 | This is a global sample showing you a bit of everything about the API: breakpoints, registers, code patching, memory editing. 3 | 4 | ![testing](result.png) 5 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/swig/generate_python_module.bat: -------------------------------------------------------------------------------- 1 | ..\..\SWIG\swig.exe -outdir ..\..\ollyapi -o ..\ollydbg2-python-bindings-swig\ollydbg2-swig_wrap.c -cpperraswarn -python ollydbg2-swig.i 2 | correct_cdecl_fpointers.py -------------------------------------------------------------------------------- /samples/int3_exception/README.md: -------------------------------------------------------------------------------- 1 | # Samples - int3_exception 2 | An example of how you can pass exception to the debuggee. The example uses a simple & well-known int3 anti-debugger trick. 3 | 4 | ![int3_exception](result.png) 5 | -------------------------------------------------------------------------------- /samples/disassemble_assemble/README.md: -------------------------------------------------------------------------------- 1 | # Samples - disassemble_assemble 2 | This time we are playing with the internal x86 assembler / disassembler. You can ask him to assemble or to disassemble, works pretty well. 3 | 4 | ![disassemble_assemble](result.png) 5 | -------------------------------------------------------------------------------- /samples/breakpoints/README.md: -------------------------------------------------------------------------------- 1 | # Samples - breakpoints 2 | This is an example made to show you how you can use SoftwareBreakpoint and HardwareBreakpoint. The demo program is doing a simple string-decoding operation at runtime. 3 | 4 | ![breakpoints](result.png) 5 | -------------------------------------------------------------------------------- /samples/import_ida_symbols/README.md: -------------------------------------------------------------------------------- 1 | # Samples - import_ida_symbols 2 | It is usually annoying to not have symbols/comments you did add on IDA in OllyDbg2. Here is an example of how you can import them with a simple script. 3 | 4 | ![import_ida_symbols](result.png) 5 | -------------------------------------------------------------------------------- /samples/upx_oep_finder/README.md: -------------------------------------------------------------------------------- 1 | # Samples - upx_oep_finder 2 | This example focuses more the functions that can be useful when looking for specific information/unpacking binaries. This example finds the original entry-point of a binary UPXed. 3 | 4 | ![upx_oep_finder](result.png) 5 | -------------------------------------------------------------------------------- /samples/gdbserver/README.md: -------------------------------------------------------------------------------- 1 | # Samples - gdbserver 2 | Here is a really tiny(/buggy?) GDB stub server. The purpose is to control OllyDbg2 remotely with a GDB client: it can be GDB itself, or IDA for example. 3 | Don't use it for doing real stuff, it's a fun PoC. 4 | 5 | ![gdbserver](result.png) 6 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/window.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_HPP 2 | #define WINDOW_HPP 3 | 4 | #include 5 | 6 | BOOL CreateCommandLineWindow(HWND hParent, HINSTANCE hInst); 7 | 8 | #define CLI_WINDOW_CLASS_NAME L"CommandLineClass" 9 | #define IDC_MAIN_EDIT 101 10 | 11 | #endif -------------------------------------------------------------------------------- /samples/call_stack/README.md: -------------------------------------------------------------------------------- 1 | # Samples - Call_stack 2 | Here the purpose is to show you the call stack display function. We have a binary with several nested calls, we put a breakpoint at the end and we check the call stack. To enhance the debugging experience, we even add some user label, sweet. 3 | 4 | ![call_stack](result.png) 5 | -------------------------------------------------------------------------------- /samples/hackyou300_dumped/README.md: -------------------------------------------------------------------------------- 1 | # Samples - hackyou300_dumped 2 | This is a practical example of how you can use OllyDbg2-python to crack wide open CTF tasks. This example uses a breakpoint manager with a dispatch handler. Each time a specific breakpoint hits, a specific callback is called in order to retrieve some information, some states of the program. 3 | 4 | ![hackyou300_dumped](result.png) 5 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/README.md: -------------------------------------------------------------------------------- 1 | In this directory you will find several important things: 2 | 3 | 1. The files needed to build OllyDbg2 plugins: the ollydbg.lib file, and the plugin.h file 4 | 2. The python-loader project: this is an OllyDbg2 plugin that allows to load some Python into your favorite debugger 5 | 3. The python-binding-swig project: this is the project that will expose OllyDbg2's API to Python. -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/toolbox.cpp: -------------------------------------------------------------------------------- 1 | #include "toolbox.hpp" 2 | 3 | std::string widechar_to_multibytes(std::wstring &st) 4 | { 5 | // XXX: make sure st doesn't have any accent.. 6 | std::string ret; 7 | ret.assign(st.begin(), st.end()); 8 | return ret; 9 | } 10 | 11 | std::wstring multibytes_to_widechar(std::string &st) 12 | { 13 | std::wstring ret; 14 | ret.assign(st.begin(), st.end()); 15 | return ret; 16 | } -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/toolbox.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TOOLBOX_HPP 2 | #define TOOLBOX_HPP 3 | 4 | #include 5 | 6 | /* 7 | Convert a std::string in std::wstring 8 | 9 | Really useful because OllyDBG2 API only works with wchar_t 10 | and the Python API only with char ; so you regularly have to 11 | play with both types. 12 | */ 13 | std::string widechar_to_multibytes(std::wstring &st); 14 | 15 | /* 16 | Convert a std::wstring in std::string 17 | */ 18 | std::wstring multibytes_to_widechar(std::string &st); 19 | 20 | #endif -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/python-loader.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python-loader", "python-loader.vcxproj", "{8826DC87-EF71-4890-B792-BCF92BD74018}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {8826DC87-EF71-4890-B792-BCF92BD74018}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {8826DC87-EF71-4890-B792-BCF92BD74018}.Debug|Win32.Build.0 = Debug|Win32 14 | {8826DC87-EF71-4890-B792-BCF92BD74018}.Release|Win32.ActiveCfg = Release|Win32 15 | {8826DC87-EF71-4890-B792-BCF92BD74018}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-bindings-swig/python-bindings-swig.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python-bindings-swig", "python-bindings-swig.vcxproj", "{D67ABC36-EB51-45F4-9704-A27721995853}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {D67ABC36-EB51-45F4-9704-A27721995853}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {D67ABC36-EB51-45F4-9704-A27721995853}.Debug|Win32.Build.0 = Debug|Win32 14 | {D67ABC36-EB51-45F4-9704-A27721995853}.Release|Win32.ActiveCfg = Release|Win32 15 | {D67ABC36-EB51-45F4-9704-A27721995853}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/swig/ollydbg2-swig.i: -------------------------------------------------------------------------------- 1 | %module python_bindings_swig 2 | %{ 3 | #include "..\inc\plugin.h" 4 | %} 5 | 6 | // http://www.swig.org/Doc1.3/Library.html#Library_carrays 7 | %include 8 | // http://www.swig.org/Doc1.3/Library.html#Library_nn7 9 | %include 10 | // http://www.swig.org/Doc1.3/Python.html#Python_nn75 && http://swig.10945.n7.nabble.com/Functions-writing-binary-data-into-buffer-td10714.html 11 | // For Readmemory 12 | %include 13 | 14 | %include 15 | %include 16 | 17 | // From http://stackoverflow.com/questions/12686400/how-to-use-swig-to-treat-a-void-function-parameter-as-python-string 18 | // Useful for Writememory 19 | %typemap(in) const void* = const char*; 20 | // Useful for Expression 21 | %typemap(in) uchar* = char*; 22 | 23 | // From http://www.swig.org/Doc1.3/Library.html#Library_carrays 24 | %array_class(ulong, ulongArray); 25 | %array_class(t_asmmod, t_asmmodArray); 26 | %array_class(t_secthdr, t_secthdrArray); 27 | #define HANDLE ulong 28 | 29 | %include "..\inc\plugin-swig.h" -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-bindings-swig/python-bindings-swig.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Fichiers sources 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/int3_exception/int3_exception.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # int3_exception.py - A very simple script to show how to deal with exceptions. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | 23 | def main(): 24 | print 'OK, you have a classic int3 anti-dbg ; you must pass the exception!' 25 | bp = SoftwareBreakpoint(0x00401044) 26 | # Run, we will hit the first int3 27 | Run__() 28 | 29 | # Now we are on the int3, we can pass the exception to olly 30 | Run__(pass_exception = 1) 31 | bp.remove() 32 | 33 | print 'Here you go!' 34 | return 1 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /samples/import_ida_symbols/import_ida_symbols.map: -------------------------------------------------------------------------------- 1 | 2 | 3 | Address Publics by Value 4 | 5 | 0001:00000020 crt_main 6 | 0001:00000130 start 7 | 0001:00000318 f_function 8 | 0001:00000331 g_function 9 | 0001:00000351 kikoo_function 10 | 0001:00000371 main_function 11 | 0001:0000099C __getmainargs 12 | 0001:000009A4 __p__fmode 13 | 0001:000009AC __p__environ 14 | 0001:000009B4 _cexit 15 | 0001:000009BC _setmode 16 | 0001:000009C4 signal 17 | 0001:000009CC puts 18 | 0001:000009D4 printf 19 | 0001:000009DC memcpy 20 | 0001:000009E4 _assert 21 | 0001:000009EC malloc 22 | 0001:000009FC free 23 | 0001:00000A04 SetUnhandledExceptionFilter 24 | 0001:00000A0C ExitProcess 25 | 0001:00000A14 GetModuleHandleA 26 | 0001:00000A1C GetProcAddress 27 | 0001:00000A24 VirtualQuery 28 | 0001:00000A2C VirtualProtect 29 | 0001:00000A34 GetAtomNameA 30 | 0001:00000A3C FindAtomA 31 | 0001:00000A44 AddAtomA 32 | 0003:00000000 aLibgcj_s_dll 33 | 0003:0000000D a_jv_registercl 34 | 0003:00000024 aHeyIMF 35 | 0003:0000002F aHeyIMG 36 | 0003:0000003A aLul 37 | 0003:0000003E aHeyS 38 | 0003:00000047 aZboobz 39 | 0003:00000050 a____RuntimePse 40 | 0003:00000070 aVirtualqueryAd 41 | 0003:000000A0 a________Gcc4_4 42 | 0003:000000E8 a0CouldnTRetrie 43 | 0003:00000124 aRetSizeSizeof_ 44 | 0003:00000170 a0CouldnTAddGcc 45 | 0003:000001A0 aGcclibcygmingE 46 | 47 | Program entry point at 0001:00000130 48 | -------------------------------------------------------------------------------- /samples/call_stack/call_stack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # call_stack.py - A simple script to display the call stack 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | 23 | def main(): 24 | # We need to add some symbol to our sample: 25 | AddUserLabel(0x00401090, 'main') 26 | AddUserLabel(0x00401070, 'f') 27 | AddUserLabel(0x00401050, 'zerg') 28 | AddUserLabel(0x00401020, 'bla') 29 | AddUserLabel(0x00401000, 'r') 30 | 31 | # Goto in the deepest routine 32 | b = SoftwareBreakpoint(ResolveApiAddress('call_stack', 'r')) 33 | Run__() 34 | b.remove() 35 | 36 | # now goto deep in printf 37 | b = SoftwareBreakpoint(ResolveApiAddress('ntdll', 'RtlEnterCriticalSection')) 38 | Run__() 39 | b.remove() 40 | 41 | # OK now the call stack should be interesting!1!& 42 | display_call_stack() 43 | return 1 44 | 45 | if __name__ == '__main__': 46 | main() -------------------------------------------------------------------------------- /samples/layers/layers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # layers.py - A script to bypass the layers in layers.exe 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | from binascii import unhexlify 23 | from time import time 24 | 25 | def main(): 26 | t1 = time() 27 | n_layers = 0 28 | pattern_layer = 'E800000000' # call $+1 29 | are_we_done = False 30 | 31 | while are_we_done == False: 32 | pattern = ReadMemory(5) 33 | if pattern != unhexlify(pattern_layer): 34 | are_we_done = True 35 | else: 36 | end_current_layer = FindHexInPage('E2FA') # LOOP 37 | assert(end_current_layer != 0) 38 | 39 | b = HardwareBreakpoint(end_current_layer + 2) 40 | Run__() 41 | b.remove() 42 | n_layers += 1 43 | 44 | print "OK, I think I've passed all the %d layers (in %f s), HF!" % (n_layers, time() - t1) 45 | return 1 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/swig/correct_cdecl_fpointers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # correct_cdecl_fpointer.py - Correct the declaraction of __cdecl function pointers 6 | # Copyright (C) 2013 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | 24 | def main(argc, argv): 25 | path = r'..\ollydbg2-python-bindings-swig\ollydbg2-swig_wrap.c' 26 | r = open(path).read() 27 | patterns = [ 28 | ('int (*arg4)(__cdecl *)', 'int (__cdecl *arg4)'), 29 | ('= (int (*)(__cdecl *)', '= (int (__cdecl *)') 30 | ] 31 | for old, new in patterns: 32 | r = r.replace(old, new) 33 | 34 | # Somehow in Readmemory there is a typo: 35 | # arg1 = (char *) buf 36 | # is generated instead of 37 | # arg1 = (char*) buf1 38 | r = r.replace('arg1 = (char *) buf;', 'arg1 = (char*) buf1;') 39 | r = r.replace('arg3 = (uchar *) buf;', 'arg3 = (uchar *) buf3;') 40 | open(path, 'w').write(r) 41 | return 0 42 | 43 | if __name__ == '__main__': 44 | sys.exit(main(len(sys.argv), sys.argv)) -------------------------------------------------------------------------------- /samples/upx_oep_finder/upx_oep_finder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # upx_oep_finder.py - A very simple script to find the original entry point of an executable upx-ed 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | 23 | def main(): 24 | print 'Looking for the popad instruction..' 25 | addr = FindInstr('popad') 26 | assert(addr != 0) 27 | 28 | print 'Found at %#.8x, goto this address!' % addr 29 | bp_popad = HardwareBreakpoint(addr) # yeah, hardware breakpoints work too! 30 | Run__() 31 | bp_popad.remove() 32 | 33 | print 'Now, looking for the JMP OEP..' 34 | addr = FindHexInPage('E9????????') 35 | assert(addr != 0) 36 | 37 | print 'Found at %#.8x, goto this address!' % addr 38 | bp_jmp = SoftwareBreakpoint(addr) 39 | Run__() 40 | bp_jmp.remove() 41 | 42 | print 'Final move, step to the OEP' 43 | StepInto() 44 | 45 | print 'You are at the OEP bro.' 46 | AddUserLabel(GetEip(), 'OriginalEntryPoint') 47 | AddUserComment(GetEip(), 'This is the original entry point.') 48 | return 1 49 | 50 | if __name__ == '__main__': 51 | main() -------------------------------------------------------------------------------- /samples/breakpoints/breakpoints.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # breakpoints.py - A very simple script to play around the breakpoints. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | 23 | def main(): 24 | print 'OK, we have an encrypted string at 0x0403024' 25 | print 'We want to know where this string is accessed (maybe decrypted!)' 26 | bp_rw = SoftwareBreakpoint(0x00401343) 27 | 28 | # this is the copy done on the local stack, move our breakpoint there 29 | Run__() 30 | bp_rw.remove() 31 | 32 | 33 | # keep in mind the hwbp stops *after* the instruction 34 | # that means you have already copied one byte from ESI to EDI 35 | # when the hwbp is hit, this is why we do GetEdi() - 1 36 | addr_s = GetEdi() 37 | bp_rw = HardwareBreakpoint(addr_s, 'w') 38 | print 'Encoded string will be copied at %#.8x' % addr_s 39 | Run__() 40 | bp_rw.remove() 41 | 42 | print 'Seems to be decrypted in this loop, goto the end of the loop' 43 | bp_loop = SoftwareBreakpoint(0x0401370, '[ESP+1C] == EAX') 44 | Run__() 45 | bp_loop.remove() 46 | 47 | print 'Dumping the size of the string on the stack..' 48 | size_s = GetEax() 49 | 50 | print 'Step to get out of the loop' 51 | StepInto() 52 | 53 | print 'Now dump the decrypted content' 54 | s = ReadMemory(size_s, addr_s) 55 | print "String decrypted: '%s'" % s 56 | 57 | print 'Now goto the RET' 58 | ExecuteUntilRet() 59 | return 1 60 | 61 | if __name__ == '__main__': 62 | main() 63 | -------------------------------------------------------------------------------- /samples/disassemble_assemble/disassemble_assemble.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # disassemble_assemble.py - Test the x86 assembler & the disassembler of OllyDBG2. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | 23 | def main(): 24 | # classic instruction: mov [esp+4], 0xdeadbeef 25 | print Disass('\xc7\x44\x24\x04\xef\xbe\xad\xde') 26 | 27 | # SSE instruction + classic instruction: movups [eax], xmm0 ; inc ecx 28 | print Disass('\x0f\x11\x00' + '\x41') 29 | 30 | # Invalid instruction 31 | try: 32 | print Disass('\xff') 33 | except Exception, e: 34 | print e 35 | 36 | try: 37 | # classic instruction + invalid one + sse instruction 38 | print Disass('\xc7\x44\x24\x04\xef\xbe\xad\xde' + '\xff' + '\x41') 39 | except Exception, e: 40 | print e 41 | 42 | # classic instruction + classic instruction + jmp -- test if the jmp is correctly disassembled 43 | print Disass('\x8B\x00' + '\x3D\x91\x00\x00\xC0' + '\x77\x3B', 0x40115C) 44 | 45 | # Now, let's play with the assembler 46 | b, size = Assemble__('mov dword ptr [eax+10], 0xdeadbeef') 47 | print repr(b) 48 | 49 | # Try to assemble random stuff 50 | try: 51 | b, size = Assemble__('where is my bawobab') 52 | except Exception, e: 53 | print e 54 | 55 | try: 56 | b, size = Assemble__('mov eax, kikoo') 57 | except Exception, e: 58 | print e 59 | 60 | b, size = Assemble__('mov dword ptr [esp+4], 0xBAB00 ; movups [eax], xmm0 ; inc ecx') 61 | print Disass(b) 62 | 63 | return 1 64 | 65 | if __name__ == '__main__': 66 | main() 67 | -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/main.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_HPP 2 | #define MAIN_HPP 3 | 4 | #include 5 | #include 6 | 7 | // Constants 8 | #define CLASS_NAME L"script_loading window" 9 | #define NAME_PLUGIN L"[python-loader]" 10 | 11 | #define MENU_LOAD_SCRIPT_IDX 1 12 | #define MENU_ABOUT_IDX 2 13 | #define MENU_CMDLINE_IDX 3 14 | 15 | #define WINDOW_BUTTON_OK_IDX 0x8801 16 | #define WINDOW_EDITBOX_IDX 0x8802 17 | 18 | 19 | // Prototypes 20 | /* 21 | Dll Main, will be called when the library will be loaded in memory by OllyDbg 22 | */ 23 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); 24 | 25 | /* 26 | Spawn a nice window to ask you the path of 27 | the python script you want to execute inside OllyDBG. 28 | */ 29 | void spwan_window(void); 30 | 31 | /* 32 | Method called by OllyDBG in order to dispatch the actions done on 33 | the menu the plugin creates. 34 | */ 35 | int handle_menu(t_table* pTable, wchar_t* pName, ulong index, int nMode); 36 | 37 | /* 38 | Execute a python script located on your file system thanks to 39 | the python high level API. 40 | */ 41 | DWORD WINAPI execute_python_script(LPVOID param); 42 | 43 | 44 | // Global variables 45 | HINSTANCE g_hinst = 0; 46 | 47 | /* 48 | typedef struct t_menu // Menu descriptor 49 | { 50 | wchar_t *name; // Menu command 51 | wchar_t *help; // Explanation of command 52 | int shortcutid; // Shortcut identifier, K_xxx 53 | MENUFUNC *menufunc; // Function that executes menu command 54 | struct t_menu *submenu; // Pointer to descriptor of popup menu 55 | union { 56 | ulong index; // Argument passed to menu function 57 | HMENU hsubmenu; // Handle of pulldown menu 58 | }; 59 | } t_menu; 60 | */ 61 | t_menu g_MainMenu[] = 62 | { 63 | { 64 | L"Load your script", 65 | L"Load in OllyDBG your custom python script.", 66 | KK_DIRECT | KK_ALT | VK_F7, handle_menu, NULL, MENU_LOAD_SCRIPT_IDX 67 | }, 68 | { 69 | L"Open the command-line window", 70 | L"Open the command-line window to run python one-liner.", 71 | KK_DIRECT | KK_ALT | VK_F8, handle_menu, NULL, MENU_CMDLINE_IDX 72 | }, 73 | { 74 | L"About", 75 | L"Fire the about messagebox.", 76 | K_NONE, handle_menu, NULL, MENU_ABOUT_IDX 77 | }, 78 | { NULL, NULL, K_NONE, NULL, NULL, 0 } 79 | }; 80 | 81 | #endif -------------------------------------------------------------------------------- /samples/testing/testing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # testing.py - A very simple script to show the stuff you can do with the python engine. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | # XXX: you must restart the script, to set correctly the arguments 23 | 24 | def main(): 25 | print 'Aight, ready for the demonstration' 26 | print 'First, here are the CPU register of the process:' 27 | display_all_registers() 28 | 29 | print 'Then, those are the PE sections of your process:' 30 | s = GetPESections() 31 | for i in s: 32 | print '%s - %#.8x' % (i.sectname, i.base) 33 | 34 | print 'Now, adding more semantic to your disassembly' 35 | AddUserComment(GetEntryPoint(), 'OK dude, actually this is the entry point.') 36 | AddUserLabel(GetEntryPoint(), 'EntryPoint') 37 | 38 | print 'Set an argument for the debuggee' 39 | SetArguments('mypwd') 40 | 41 | print 'OK, where is msvcrt.strcmp ?' 42 | strcmp_address = ResolveApiAddress('msvcrt', 'strcmp') 43 | 44 | print 'Located at %#.8x, Lets go!' % strcmp_address 45 | bp = SoftwareBreakpoint(strcmp_address) 46 | Run__() 47 | bp.remove() 48 | 49 | print 'Dumping the address of the password on the stack ([ARG2])' 50 | pp_pass = GetEsp() + 4 + 4 51 | 52 | print 'Now getting the address of the password' 53 | p_pass = ReadDwordMemory(pp_pass) 54 | 55 | print 'Perfect, time to dump the password located at %#.8x, here it is:' % p_pass 56 | print ReadMemory(18, p_pass) 57 | 58 | print 'Now executing the function until the RET..' 59 | ExecuteUntilRet() 60 | 61 | print 'Modifying the return-value of the function' 62 | SetEax(0) 63 | 64 | # pass over the ret + test eax, eax + jne 65 | for i in range(3): 66 | StepInto() 67 | 68 | print "Yeah you're on the good-boy branch, time to hijack the content displayed!" 69 | print 'Writing at 0x00402070 a new string..' 70 | WriteMemory('hijacked printf!\x00', 0x00402070) 71 | 72 | print 'Patching the code at EIP to place our address on the stack correctly..' 73 | PatchCode('mov dword [esp], 0x00402070') 74 | 75 | ExecuteUntilRet() 76 | print "I guess it's done, hope you enjoyed it!" 77 | return 1 78 | 79 | if __name__ == '__main__': 80 | main() 81 | -------------------------------------------------------------------------------- /samples/hackyou300_dumped/hackyou_re300_dumped.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # hackyou_re300.py - A little script to show how you could pwn the reverse300 6 | # challenge from http://hackyou.ctf.su 2012. 7 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | from ollyapi import * 23 | password, password_len = '', 0 24 | 25 | class HitBreakpointManager(): 26 | """Trigger callback when hitting a breakpoint""" 27 | def __init__(self): 28 | self.breakpoints = [ 29 | ] 30 | 31 | def add(self, bp, callback): 32 | """Add a breakpoint/callback in the hit list""" 33 | # bp must be an instance of Breakpoint 34 | assert(isinstance(bp, Breakpoint) == True) 35 | 36 | self.breakpoints.append({ 37 | 'instance' : bp, 38 | 'address' : bp.get_address(), 39 | 'callback' : callback 40 | }) 41 | 42 | def clean(self): 43 | """Remove properly the breakpoints""" 44 | for entry in self.breakpoints: 45 | entry['instance'].remove() 46 | 47 | def go(self): 48 | """Launch the process until the process is dead""" 49 | while IsDebuggeeFinished() != True: 50 | Run__() 51 | for entry in self.breakpoints: 52 | if entry['address'] == GetEip(): 53 | # We can launch the callback associated with this breakpoint 54 | entry['callback']() 55 | 56 | self.clean() 57 | 58 | # thx awe o\ -- http://blog.w3challs.com/index.php?post/2012/10/13/HackYou-CTF-Reverse100%2C-Reverse200%2C-Reverse300-Writeups 59 | 60 | def callback_compare_password(): 61 | global password, password_len 62 | 63 | SetEdx(GetEax()) 64 | 65 | if password_len in (4, 9): 66 | password += '-' 67 | password_len += 1 68 | 69 | password += chr(GetEax()) 70 | password_len += 1 71 | 72 | print '[+] %s' % password 73 | 74 | def main(): 75 | # Setting a fake serial in argument 76 | SetArguments('hackyou 0123-4567-8910') 77 | 78 | # Now lets get the password 79 | manager = HitBreakpointManager() 80 | 81 | # http://blog.w3challs.com/index.php?post/2012/10/13/HackYou-CTF-Reverse100%2C-Reverse200%2C-Reverse300-Writeups 82 | manager.add(SoftwareBreakpoint(0x401113), callback_compare_password) 83 | manager.add(SoftwareBreakpoint(0x4011D5), callback_compare_password) 84 | manager.add(SoftwareBreakpoint(0x401297), callback_compare_password) 85 | 86 | manager.go() 87 | 88 | print 'OK, here is the serial: %s' % password 89 | return 1 90 | 91 | if __name__ == '__main__': 92 | main() -------------------------------------------------------------------------------- /samples/import_ida_symbols/import_ida_symbols.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # import_ida_symbols.py - A script to import the IDA symbols (.map file) in OllyDbg2 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | 23 | def get_name_address_symbols(f): 24 | """ 25 | Create a list of dictionnaries storing an absolute address and the symbol name: 26 | [{ 27 | 'name' : 'sub_bla', 28 | 'addr' : 1337 29 | }, ..] 30 | 31 | Here is the how a .MAP file generated by IDA looks like: 32 | [...] 33 | Address Publics by Value 34 | 35 | 0001:00000000 sub_401000 36 | 0001:00000010 sub_401010 37 | 0001:0000002A loc_40102A 38 | 0001:00000040 sub_401040 39 | 0001:00000090 loc_401090 40 | [...] 41 | """ 42 | # we need to retrieve the PE sections of the binary, in order to have the base of the different segments 43 | sections = GetPESections() 44 | 45 | # the state used to parse correctly the .map 46 | state = None 47 | symbols = [] 48 | 49 | for line in f.readlines(): 50 | line = line.strip() 51 | 52 | # we found the begining of the interesting part of the file 53 | if line.startswith('Address'): 54 | state = 'PreMarkerOK' 55 | continue 56 | 57 | # this is the empty line just after the begining of the intersting part 58 | if line == '' and state == 'PreMarkerOK': 59 | state = 'MarkerOK' 60 | continue 61 | 62 | # the end! 63 | if line == '' and state == 'MarkerOK': 64 | break 65 | 66 | if state == 'MarkerOK': 67 | # retrieve the full address (segment selector + RVA) and the name of the symbol 68 | full_addr, name = line.split(' ', 2) 69 | 70 | # extract the segment selector and the RVA 71 | segment_selector, addr = full_addr.split(':', 2) 72 | 73 | # compute the absolute address (base of the segment (retrieved in the PE sections) + RVA) 74 | absolute_addr = sections[int(segment_selector) - 1].base + int(addr, 16) 75 | 76 | # feed the symbols list 77 | symbols.append({ 78 | 'name' : name, 79 | 'addr' : absolute_addr 80 | }) 81 | 82 | return symbols 83 | 84 | def main(): 85 | mapfile_path = r'D:\Codes\OllyDBG2-Python\samples\import_ida_symbols\import_ida_symbols.map' 86 | with open(mapfile_path, 'r') as f: 87 | # get a simple list with the absolute address & the name of our symbols 88 | pair_address_symbol = get_name_address_symbols(f) 89 | for symbol in pair_address_symbol: 90 | # yay, add them to ollydbg 91 | AddUserLabel(symbol['addr'], symbol['name']) 92 | 93 | return 1 94 | 95 | if __name__ == '__main__': 96 | main() -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.hpp" 2 | #include "plugin.h" 3 | #include "toolbox.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | BOOL class_is_already_registered = FALSE; 10 | 11 | LRESULT CALLBACK CommandLineWinProc( 12 | _In_ HWND hwnd, 13 | _In_ UINT uMsg, 14 | _In_ WPARAM wParam, 15 | _In_ LPARAM lParam 16 | ) 17 | { 18 | switch (uMsg) 19 | { 20 | case WM_CREATE: 21 | // Initialize the window. 22 | return 0; 23 | 24 | case WM_PAINT: 25 | // Paint the window's client area. 26 | ValidateRect(hwnd, 0); 27 | break; 28 | 29 | case WM_DESTROY: 30 | // Clean up window-specific data objects. 31 | return 0; 32 | 33 | case WM_CLOSE: 34 | { 35 | DestroyWindow(hwnd); 36 | break; 37 | } 38 | 39 | case WM_COMMAND: 40 | { 41 | switch(LOWORD(wParam)) 42 | { 43 | case IDC_MAIN_EDIT: 44 | { 45 | switch(HIWORD(wParam)) 46 | { 47 | case CBN_SELENDOK: 48 | { 49 | DWORD len = ComboBox_GetTextLength(GetDlgItem(hwnd, IDC_MAIN_EDIT)); 50 | if(len == 0) 51 | break; 52 | 53 | wchar_t *buffer = (wchar_t*) malloc((len + 1) * sizeof(wchar_t)); 54 | if(buffer == NULL) 55 | break; 56 | 57 | SecureZeroMemory(buffer, (len + 1) * sizeof(wchar_t)); 58 | 59 | ComboBox_GetText( 60 | GetDlgItem(hwnd, IDC_MAIN_EDIT), 61 | buffer, 62 | len + 1 63 | ); 64 | 65 | std::string cmd(widechar_to_multibytes(std::wstring(buffer))); 66 | 67 | PyRun_SimpleString(cmd.c_str()); 68 | 69 | int idx = ComboBox_FindStringExact( 70 | GetDlgItem(hwnd, IDC_MAIN_EDIT), 71 | 0, 72 | buffer 73 | ); 74 | 75 | if(idx == CB_ERR) 76 | ComboBox_AddString( 77 | GetDlgItem(hwnd, IDC_MAIN_EDIT), 78 | buffer 79 | ); 80 | 81 | free(buffer); 82 | break; 83 | } 84 | } 85 | 86 | break; 87 | } 88 | } 89 | 90 | break; 91 | } 92 | 93 | // 94 | // Process other messages. 95 | // 96 | 97 | default: 98 | return DefWindowProc(hwnd, uMsg, wParam, lParam); 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | BOOL CreateCommandLineWindow(HWND hParent, HINSTANCE hInst) 105 | { 106 | HWND hCmdLine, hEdit; 107 | RECT rect = {0}; 108 | WNDCLASS wClass = {0}; 109 | 110 | wClass.lpfnWndProc = CommandLineWinProc; 111 | wClass.hInstance = hInst; 112 | wClass.lpszClassName = CLI_WINDOW_CLASS_NAME; 113 | wClass.hbrBackground = (HBRUSH)(16); 114 | 115 | if(RegisterClass(&wClass) == 0 && class_is_already_registered == FALSE) 116 | return FALSE; 117 | 118 | if(class_is_already_registered == FALSE) 119 | class_is_already_registered = TRUE; 120 | 121 | GetWindowRect(hParent, &rect); 122 | 123 | hCmdLine = CreateWindowEx( 124 | WS_EX_LTRREADING, 125 | CLI_WINDOW_CLASS_NAME, 126 | L"Command Line Window", 127 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 128 | 0, 129 | rect.bottom - 70, 130 | 530, 131 | 60, 132 | hParent, 133 | NULL, 134 | hInst, 135 | NULL 136 | ); 137 | 138 | if(hCmdLine == NULL) 139 | return FALSE; 140 | 141 | hEdit = CreateWindowEx( 142 | WS_EX_OVERLAPPEDWINDOW, 143 | WC_COMBOBOX, 144 | L"", 145 | WS_VISIBLE | WS_CHILD | CBS_SIMPLE | CBS_DISABLENOSCROLL, 146 | 0, 147 | 0, 148 | 530, 149 | 100, 150 | hCmdLine, 151 | (HMENU)IDC_MAIN_EDIT, 152 | hInst, 153 | NULL 154 | ); 155 | 156 | if(hEdit == NULL) 157 | return FALSE; 158 | 159 | return TRUE; 160 | } -------------------------------------------------------------------------------- /ollyapi/memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # memory.py - High level API to manipulate memory. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from python_bindings_swig import * 22 | import threads 23 | import utils 24 | 25 | from struct import unpack as u 26 | from binascii import unhexlify 27 | 28 | # Wrappers 29 | 30 | def FlushMemoryCache(): 31 | """ 32 | Flush the intern memory cache of OllyDBG2 33 | """ 34 | Flushmemorycache() 35 | 36 | def WriteMemory(buff, addr = None, mode = 0): 37 | """ 38 | Write directly in the memory of the process 39 | """ 40 | 41 | # XXX: check if memory exists 42 | if addr == None: 43 | addr = threads.GetEip() 44 | 45 | n = Writememory( 46 | buff, 47 | addr, 48 | len(buff), 49 | mode 50 | ) 51 | 52 | # flush the cache after writing ; not sure it's good/required to do that though. 53 | FlushMemoryCache() 54 | 55 | return n 56 | 57 | def ReadMemory(size, addr = None, mode = 0): 58 | """ 59 | Read the memory of the process at a specific address 60 | """ 61 | # XXX: test if the address exists 62 | if addr == None: 63 | addr = threads.GetEip() 64 | 65 | b = bytearray(size) 66 | n = Readmemory( 67 | b, 68 | addr, 69 | size, 70 | mode 71 | ) 72 | 73 | # XXX: Hmm, don't care about n right ? 74 | return str(b) 75 | 76 | def Expression_(result, expression, data, base, size, threadid, a, b, mode): 77 | """ 78 | Let OllyDbg evaluate an expression for you: 79 | * get an exported function address easily thanks to the notation module.function_name 80 | """ 81 | r = Expression( 82 | result, 83 | expression, 84 | data, 85 | base, 86 | size, 87 | threadid, 88 | a, 89 | b, 90 | mode 91 | ) 92 | 93 | if result.value == 'Unrecognized identifier': 94 | return None 95 | 96 | return r 97 | 98 | def FindMemory(addr): 99 | """ 100 | Find a structure t_memory describing the memory addr points to 101 | """ 102 | return Findmemory(addr) 103 | 104 | 105 | # Abstraction 106 | 107 | def ResolveApiAddress(module, function): 108 | """ 109 | Get the address of a specific API exported by a specific module thanks to their names 110 | 111 | Note: 112 | - you can use '' as a function name to resolve the entry point of a specific module 113 | """ 114 | r = t_result() 115 | ret = Expression_( 116 | r, 117 | '%s.%s' % (module, function), 118 | '', 119 | 0, 120 | 0, 121 | threads.GetCpuThreadId(), 122 | 0, 123 | 0, 124 | 0 125 | ) 126 | 127 | if r.datatype == EXPR_DWORD: 128 | return r.u.u 129 | 130 | return None 131 | 132 | def ReadDwordMemory(address = None): 133 | """ 134 | Read a dword in memory 135 | """ 136 | if address == None: 137 | address = threads.GetEip() 138 | 139 | data = ReadMemory(4, address) 140 | return u(' 6 | #include 7 | #include 8 | 9 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) 10 | { 11 | if(fdwReason == DLL_PROCESS_ATTACH) 12 | g_hinst = hinstDLL; 13 | 14 | return TRUE; // Successful DLL_PROCESS_ATTACH. 15 | } 16 | 17 | /* 18 | This routine is required by the OllyDBG plugin engine! 19 | */ 20 | extc int __cdecl ODBG2_Pluginquery(int ollydbgversion, ulong *features, wchar_t pluginname[SHORTNAME], wchar_t pluginversion[SHORTNAME]) 21 | { 22 | // Yeah, the plugin interface in the v1/v2 are different 23 | if(ollydbgversion < 201) 24 | return 0; 25 | 26 | // Set plugin name and version 27 | wcscpy_s(pluginname, SHORTNAME, L"python-loader"); 28 | wcscpy_s(pluginversion, SHORTNAME, L"v0.1"); 29 | 30 | // Initialize the python environment, prepare the hooks 31 | Py_Initialize(); 32 | PyEval_InitThreads(); 33 | 34 | Addtolist(0x31337, RED, NAME_PLUGIN L" Plugin fully initialized."); 35 | 36 | return PLUGIN_VERSION; 37 | } 38 | 39 | extc void __cdecl ODBG2_Plugindestroy(void) 40 | { 41 | // Properly ends the python environment 42 | Py_Finalize(); 43 | } 44 | 45 | /* 46 | Adds items to OllyDbgs menu system. 47 | */ 48 | extc t_menu * __cdecl ODBG2_Pluginmenu(wchar_t *type) 49 | { 50 | if(wcscmp(type, PWM_MAIN) == 0) 51 | return g_MainMenu; 52 | 53 | return NULL; 54 | } 55 | 56 | void spawn_window(void) 57 | { 58 | wchar_t *file_path = (wchar_t*)malloc(sizeof(wchar_t) * 1024); 59 | OPENFILENAME ofn = {0}; 60 | 61 | if(file_path == NULL) 62 | return; 63 | 64 | ofn.lStructSize = sizeof(ofn); 65 | ofn.hwndOwner = hwollymain; 66 | ofn.lpstrFile = file_path; 67 | // Set lpstrFile[0] to '\0' so that GetOpenFileName does not 68 | // use the contents of szFile to initialize itself. 69 | ofn.lpstrFile[0] = '\0'; 70 | ofn.nMaxFile = 1024 - 1; 71 | ofn.lpstrFilter = L"Python files\0*.py\0\0"; 72 | ofn.nFilterIndex = 1; 73 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; 74 | 75 | if(GetOpenFileName(&ofn) == TRUE) 76 | { 77 | /* 78 | XXX: Seems to not work when instrumenting OllyDBG2 ; 79 | I think the reason is: 80 | When you call ollydbg!Run it calls kernelbase!ContinueDebugEvent 81 | "Only the thread that created dwProcessId with the CreateProcess function can call ContinueDebugEvent." 82 | 83 | So when, calling ollydbg!Run from another is wrong ; same thing for ollydbg!Checkfordebugevent. 84 | 85 | CreateThread( 86 | NULL, 87 | 0, 88 | execute_python_script, 89 | file_path, 90 | 0, 91 | NULL 92 | ); 93 | */ 94 | execute_python_script(file_path); 95 | } 96 | } 97 | 98 | int handle_menu(t_table* pTable, wchar_t* pName, ulong index, int nMode) 99 | { 100 | if(nMode == MENU_VERIFY) 101 | return MENU_NORMAL; 102 | else if(nMode == MENU_EXECUTE) 103 | { 104 | switch(index) 105 | { 106 | case MENU_LOAD_SCRIPT_IDX: 107 | { 108 | spawn_window(); 109 | break; 110 | } 111 | 112 | case MENU_ABOUT_IDX: 113 | { 114 | MessageBox( 115 | hwollymain, 116 | L"python loader", 117 | L"About python-loader", 118 | MB_OK| MB_ICONINFORMATION 119 | ); 120 | 121 | break; 122 | } 123 | 124 | case MENU_CMDLINE_IDX: 125 | { 126 | if(CreateCommandLineWindow(hwollymain, g_hinst) == FALSE) 127 | Addtolist(0x31337, RED, NAME_PLUGIN L" The command-line window can't be created."); 128 | 129 | break; 130 | } 131 | 132 | default: 133 | break; 134 | } 135 | 136 | return MENU_NOREDRAW; 137 | } 138 | else 139 | return MENU_ABSENT; 140 | } 141 | 142 | DWORD WINAPI execute_python_script(LPVOID param) 143 | { 144 | wchar_t *path = (wchar_t*)param; 145 | Addtolist(0, WHITE, NAME_PLUGIN L" Trying to execute the script located here: '%s'..", path); 146 | 147 | std::wstring pathW(path); 148 | std::string pathA(widechar_to_multibytes(pathW)); 149 | 150 | PyObject* PyFileObject = PyFile_FromString((char*)pathA.c_str(), "r"); 151 | if(PyFileObject == NULL) 152 | { 153 | Addtolist(0, RED, NAME_PLUGIN L" Your file doesn't exist."); 154 | goto clean; 155 | } 156 | 157 | PyRun_SimpleFile(PyFile_AsFile(PyFileObject), (char*)pathA.c_str()); 158 | 159 | Addtolist(0, WHITE, NAME_PLUGIN L" Execution is done!"); 160 | 161 | clean: 162 | free(path); 163 | return 1; 164 | } -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-bindings-swig/python-bindings-swig.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {D67ABC36-EB51-45F4-9704-A27721995853} 15 | Win32Proj 16 | pythonbindingsswig 17 | 18 | 19 | 20 | DynamicLibrary 21 | true 22 | v110 23 | Unicode 24 | 25 | 26 | DynamicLibrary 27 | false 28 | v110 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | 51 | 52 | Level3 53 | Disabled 54 | WIN32;_DEBUG;_WINDOWS;_USRDLL;PYTHONBINDINGSSWIG_EXPORTS;%(PreprocessorDefinitions) 55 | 56 | 57 | Windows 58 | true 59 | 60 | 61 | 62 | 63 | Level3 64 | 65 | 66 | MaxSpeed 67 | true 68 | true 69 | WIN32;NDEBUG;_WINDOWS;_USRDLL;PYTHONBINDINGSSWIG_EXPORTS;%(PreprocessorDefinitions) 70 | C:\Program Files %28x86%29\Python\Python275\include;%(AdditionalIncludeDirectories) 71 | /J %(AdditionalOptions) 72 | 73 | 74 | Windows 75 | true 76 | true 77 | true 78 | ..\lib;C:\Program Files %28x86%29\Python\Python275\libs;%(AdditionalLibraryDirectories) 79 | ollydbg.lib;python27.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 80 | ..\..\ollyapi\_python_bindings_swig.pyd 81 | 82 | 83 | cd ..\swig\ 84 | generate_python_module.bat 85 | 86 | 87 | Launching the python bindings generation with SWIG.. 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /ollyapi/__init__.py: -------------------------------------------------------------------------------- 1 | from breakpoints import * 2 | from threads import * 3 | from utils import * 4 | from memory import * 5 | from sym import * 6 | 7 | # Let's hook sys.stdout in order to see the printed statement in OllyDbg2's Log window. 8 | import sys 9 | buffer_stderr = '' 10 | 11 | class StdStreamHooker: 12 | """ 13 | This class is able to hook the python stdout or the stderr stream. 14 | You can use it to forward all the content displayed on stdout/stderr where you want! 15 | """ 16 | def __init__(self, which_stream = 'stdout'): 17 | """ 18 | You can hook only the stdout or the stderr python stream 19 | """ 20 | assert(which_stream in ['stdout', 'stderr']) 21 | self.hook_func = None 22 | self.real_stream = None 23 | self.which_stream = which_stream 24 | 25 | def start(self, func): 26 | """ 27 | Let the show begins -- this method starts the hooking GetProcAddress 28 | """ 29 | self.hook_func = func 30 | if self.which_stream == 'stdout': 31 | self.real_stream = sys.stdout 32 | sys.stdout = self 33 | else: 34 | sys.real_stream = sys.stderr 35 | sys.stderr = self 36 | 37 | def stop(self): 38 | """ 39 | Unhook the stream you've hooked 40 | """ 41 | if self.real_stream != None: 42 | self.real_stream.flush() 43 | self.real_stream = None 44 | 45 | if self.which_stream == 'stdout': 46 | sys.stdout = sys.__stdout__ 47 | else: 48 | sys.stderr = sys.__stderr__ 49 | 50 | self.hook_func = None 51 | 52 | def write(self, s): 53 | """ 54 | This is a proxy-function that will forward the write processing to your hook method. 55 | 56 | In fact, when a piece of code will call sys.stdout.write, StdStreamHooker will be called and 57 | it will simply forward the call to your own method ; in order to whatever you need to. 58 | 59 | Little schema: 60 | sys.stdout.write -> StdStreamHooker.write -> your own method 61 | """ 62 | return self.hook_func(s) 63 | 64 | def __getattr__(self, n): 65 | """ 66 | This methods is a forwarder method ; when pieces of code will call methods don't 67 | overloaded by StdStreamHooker, you have to forward them to the original stream class. 68 | 69 | For example, if someone to stuff like sys.stdout.fflush(), the StdStreamHooker doesn't 70 | have any implementation for that method, and we don't want to reimplement the methods 71 | available in std.stdout/stderr. So here is a simple code that explains the __getattr__ trick: 72 | 73 | >>> class A(): 74 | ... def __init__(self): 75 | ... print 'hi, this is the constructor' 76 | ... def methoda(self): 77 | ... print 'this is methoda' 78 | ... def __getattr__(self, n): 79 | ... print 'getattr:n' 80 | ... 81 | >>> a = A() 82 | hi, this is the constructor 83 | >>> a.methoda() 84 | this is methoda 85 | >>> a.methodb() 86 | getattr:n 87 | [..] 88 | >>> a.method_undefined() 89 | getattr:n 90 | [..] 91 | """ 92 | return self.real_stream.__getattr__(n) 93 | 94 | def hook_method(s): 95 | """ 96 | This is the hook method for the stdout stream, it tries to 97 | respect the '\n' in your strings in order to have a clean 98 | output in the ollydbg log window 99 | """ 100 | # yeah, each time you do a print 'something' python call two times 101 | # sys.stdout.write ; first with the string 'something' and the second 102 | # with only an '\n' 103 | if len(s) == 1 and s[0] == '\n': 104 | return 105 | 106 | # now ollydbg just don't care if you have an '\n' in your string 107 | # it will just display the string added to the list on a single line 108 | # whatever the string is. 109 | # The trick is to do other calls to addtolist to emulate a '\n' 110 | chunks = s.split('\n') 111 | for substr in chunks: 112 | Addtolist(0, 0, '%s' % substr) 113 | 114 | def hook_method_stderr(s): 115 | """ 116 | Trying to bufferize the stream in order to have a proper 117 | output in the ollydbg log window. 118 | 119 | Each time you call Addtolist it displays a new line, so we keep 120 | a global buffer until we find an '\n' ; if we found one we display 121 | the whole buffer, if not we just concatenate the string to the global 122 | buffer. 123 | """ 124 | global buffer_stderr 125 | idx = s.find('\n') 126 | if idx != -1: 127 | if len(s) == 1: 128 | Addtolist(0, 0, '%s' % buffer_stderr) 129 | buffer_stderr = '' 130 | else: 131 | buffer_stderr += s[:idx] 132 | Addtolist(0, 0, '%s' % buffer_stderr) 133 | if idx != len(s): 134 | buffer_stderr = s[idx + 1:] 135 | else: 136 | buffer_stderr = '' 137 | else: 138 | # We keep in memory the buffer until finding an '\n' 139 | # in order to have a clean output in the log window 140 | buffer_stderr += s 141 | 142 | p_stdout = StdStreamHooker('stdout') 143 | p_stderr = StdStreamHooker('stderr') 144 | 145 | p_stdout.start(hook_method) 146 | p_stderr.start(hook_method_stderr) 147 | print '[hooker] Ready to execute some stuff!' -------------------------------------------------------------------------------- /ollydbg2-plugin-development-files/ollydbg2-python-loader/python-loader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {8826DC87-EF71-4890-B792-BCF92BD74018} 15 | Win32Proj 16 | pythonloader 17 | 18 | 19 | 20 | DynamicLibrary 21 | true 22 | Unicode 23 | 24 | 25 | DynamicLibrary 26 | false 27 | true 28 | Unicode 29 | v110 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | true 43 | 44 | 45 | false 46 | 47 | 48 | 49 | 50 | 51 | Level3 52 | Disabled 53 | WIN32;_DEBUG;_WINDOWS;_USRDLL;PYTHONLOADER_EXPORTS;%(PreprocessorDefinitions) 54 | C:\Program Files %28x86%29\Python\266\include;D:\Codes\OllyDBG2-Python\python-loader\inc;%(AdditionalIncludeDirectories) 55 | CompileAsCpp 56 | 57 | 58 | Windows 59 | true 60 | C:\Program Files %28x86%29\Python\266\libs;lib/;%(AdditionalLibraryDirectories) 61 | python26.lib;ollydbg.lib;%(AdditionalDependencies) 62 | 63 | 64 | 65 | 66 | Level3 67 | 68 | 69 | MaxSpeed 70 | true 71 | true 72 | WIN32;NDEBUG;_WINDOWS;_USRDLL;PYTHONLOADER_EXPORTS;%(PreprocessorDefinitions) 73 | C:\Program Files (x86)\Python\Python275\include;..\inc\;%(AdditionalIncludeDirectories) 74 | /J %(AdditionalOptions) 75 | CompileAsCpp 76 | 77 | 78 | Windows 79 | true 80 | true 81 | true 82 | C:\Program Files (x86)\Python\Python275\libs;..\lib\;%(AdditionalLibraryDirectories) 83 | python27.lib;ollydbg.lib;shlwapi.lib;%(AdditionalDependencies) 84 | ..\..\plugins\python-loader.dll 85 | NotSet 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /samples/gdbserver/gdbserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # gdbserver.py - Control OllyDBG2 with GDB over the network! (for fun) 6 | # Copyright (C) 2013 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from ollyapi import * 22 | import sys 23 | import socket 24 | import logging 25 | import struct 26 | 27 | GDB_SIGNAL_TRAP = 5 28 | 29 | def checksum(data): 30 | checksum = 0 31 | for c in data: 32 | checksum += ord(c) 33 | return checksum & 0xff 34 | 35 | # Code a bit inspired from http://mspgcc.cvs.sourceforge.net/viewvc/mspgcc/msp430simu/gdbserver.py?revision=1.3&content-type=text%2Fplain 36 | class GDBClientHandler(object): 37 | def __init__(self, clientsocket): 38 | self.clientsocket = clientsocket 39 | self.netin = clientsocket.makefile('r') 40 | self.netout = clientsocket.makefile('w') 41 | self.log = logging.getLogger('gdbclienthandler') 42 | self.last_pkt = None 43 | 44 | def close(self): 45 | '''End of story!''' 46 | self.netin.close() 47 | self.netout.close() 48 | self.clientsocket.close() 49 | self.log.info('closed') 50 | 51 | def run(self): 52 | '''Some doc about the available commands here: 53 | * http://www.embecosm.com/appnotes/ean4/embecosm-howto-rsp-server-ean4-issue-2.html#id3081722 54 | * http://git.qemu.org/?p=qemu.git;a=blob_plain;f=gdbstub.c;h=2b7f22b2d2b8c70af89954294fa069ebf23a5c54;hb=HEAD + 55 | http://git.qemu.org/?p=qemu.git;a=blob_plain;f=target-i386/gdbstub.c;hb=HEAD''' 56 | self.log.info('client loop ready...') 57 | while self.receive() == 'Good': 58 | pkt = self.last_pkt 59 | self.log.debug('receive(%r)' % pkt) 60 | # Each packet should be acknowledged with a single character. '+' to indicate satisfactory receipt 61 | self.send_raw('+') 62 | 63 | def handle_q(subcmd): 64 | ''' 65 | subcmd Supported: https://sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html#qSupported 66 | Report the features supported by the RSP server. As a minimum, just the packet size can be reported. 67 | ''' 68 | if subcmd.startswith('Supported'): 69 | self.log.info('Received qSupported command') 70 | self.send('PacketSize=%x' % 4096) 71 | elif subcmd.startswith('Attached'): 72 | self.log.info('Received qAttached command') 73 | # https://sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html 74 | self.send('0') 75 | elif subcmd.startswith('C'): 76 | self.send('T%.2x;' % GetCpuThreadId()) 77 | else: 78 | self.log.error('This subcommand %r is not implemented in q' % subcmd) 79 | self.send('') 80 | 81 | def handle_h(subcmd): 82 | self.send('OK') 83 | 84 | def handle_qmark(subcmd): 85 | self.send('S%.2x' % GDB_SIGNAL_TRAP) 86 | 87 | def handle_g(subcmd): 88 | if subcmd == '': 89 | # EAX, ECX, EDX, ESP, EBP, ESI, EDI, EIP, EFLAGS, CS, SS, DS, ES, FS, GS 90 | registers = [ 91 | GetEax(), GetEcx(), GetEdx(), GetEbx(), GetEsp(), 92 | GetEbp(), GetEsi(), GetEdi(), GetEip(), GetEflags(), 93 | GetCs(), GetSs(), GetDs(), GetEs(), GetFs(), GetGs() 94 | ] 95 | s = '' 96 | for r in registers: 97 | s += struct.pack(' 9 | OllyDbg2-Python 10 | =============== 11 | 12 | Motivations 13 | ------------ 14 | Nowadays in the reverse-engineering world, almost everything is scriptable using Python: IDA Pro, WinDbg, ImmunitDebugger, etc. The thing is OllyDbg2 wasn't. The only way to interact with OllyDbg2's API was by creating a C/C++ plugin. But we all know everything is easier in Python, that's the reason why I started this project back in 2012 summer. 15 | 16 | Under the hoods 17 | ------------ 18 | To be able to export OllyDbg2's API to Python (currently Py275), we need two important things: 19 | 20 | 1. python-loader: this is an OllyDbg2 plugin that imports the Python engine ; with that plugin you can launch some Python into your debugger 21 | 2. python-bindings-swig: this project builds the connectors you need to poke OllyDbg2's API with Python 22 | 23 | The python-loader tries also to enhance user experience by adding a command-line edit bar in order to write easily Python one-liner without loading a script. At the moment, that bar isn't working very well (I'm not a GUI expert *at all*.), but I will give it a try to build better one. 24 | 25 | The python-bindings-swig project is a bit more touchy, it is using [SWIG](http://www.swig.org/) in order to generate the bindings automatically and it seems to work pretty great so far. But SWIG can be sometimes a bit weird to play with, so if I made some mistakes don't hesitate to pull-requests corrections! 26 | 27 | Features 28 | --------- 29 | I've tried to expose the main features we would like to have when it comes to script a debugger: 30 | 31 | * CPU state inspection: get/set x86 registers, get information about segment selectors 32 | * memory: read, write in the debuggee memory ; also obtain information about specific memory regions 33 | * assembler/disassembler: interact with the internal x86 assembler/disassembler 34 | * breakpoints: easily set/remove software/hardware normal/conditionnal breakpoints wherever you want 35 | * symbols: try to use Microsoft/OllyDbg2 API to obtain symbols information (like a function name by its address) 36 | * enhance the disassembly: you can add comments and/or labels easily 37 | * looking for something in memory: there are also a couple of methods to look for some hexadecimal bytes or instructions in memory, really handy 38 | * instrument the debugger: ask the debugger to StepInto/StepOver/ExecuteUntilRet in the debuggee 39 | * etc. 40 | 41 | If you want to see real examples, check out the `samples/` directory! If you have idea of cool examples to show case the API feel free to contact me. 42 | 43 | Building python-loader 44 | ----------------------- 45 | 46 | You will need Python development files, I'm currently using Python 275. 47 | 48 | Building the Python bindings via [SWIG](http://www.swig.org/) 49 | ------------------------------------- 50 | 51 | To build the API bindings you will need [SWIG](http://www.swig.org/) and Python 275. 52 | 53 | 1. Fetch the last [Ollydbg2's development files](http://www.ollydbg.de/version2.html). Move the `plugin.h` in the `ollydbg2-plugin-development-files/inc/` directory, and the `ollydbg.lib` in `ollydbg2-plugin-development-files/lib/`. 54 | 2. Then copy the `plugin.h` to `plugin-swig.h`. Here are the things you have to change in the `plugin-swig.h` file: 55 | 56 | * Some API are declared in the `plugin.h` file, but in fact they aren't in Ollydbg2's export address table ; so comment them. Here is the list: `SetcaseA`, `SetcaseW`, `StrcopycaseA`, `StrcopycaseW`, `Strnst`, `StrnstrW`, `StrcmpW`, `Div64by32`, `CRCcalc`, `Getcpuidfeatures`, `Maskfpu`, `Clearfpu`. 57 | * Remove the `__cdecl` from the `stdapi`, `varapi`, `oddata`, `pentry`. There is also another one in `EMUFUNC`'s typedef. 58 | * Remove the `const` from the `oddata` declaration (like that you will be able to interact with internal variables) both in `plugin.h` & `plugin-swig.h`. 59 | * Remove the `_import` keyword from `oddata`'s definition. 60 | * Rename the `Readmemory`'s first argument into `char *buff`, add before `%pybuffer_mutable_string(char *buf)`, add after `%typemap(in) char *buf;`. Do the same thing with the following API: 61 | * `Disasm` and its first argument 62 | * `Assembleallforms` and its last argument 63 | * `Getanalysercomment` and its third argument 64 | * `Getproccomment` and its third argument 65 | * `Decodeaddress` and its fourth argument 66 | * `Decoderelativeoffset` and its third argument 67 | * Anonymous nested structures aren't supported, so you have to give a name to the unions in the following structure: `t_result`. 68 | 69 | 3. Open the `python-bindings-swig` project and build the Python bindings. 70 | 4. You're ready to go! 71 | 72 | 73 | Installation 74 | ============ 75 | 76 | 1. `git clone https://github.com/0vercl0k/ollydbg2-python.git` 77 | 2. Move all your `OllyDbg2` binaries in the `ollydbg2-python` directory. It should looks like this: 78 | 79 | ``` 80 | D:\tmp\ollydbg2-python>ls -la . 81 | total 3572 82 | drw-rw-rw- 8 0vercl0k 0 4096 2013-09-22 16:17 . 83 | drw-rw-rw- 5 0vercl0k 0 4096 2013-09-22 16:13 .. 84 | drw-rw-rw- 7 0vercl0k 0 4096 2013-09-22 16:13 .git 85 | -rw-rw-rw- 1 0vercl0k 0 1061944 2008-03-21 01:44 dbghelp.dll 86 | drw-rw-rw- 2 0vercl0k 0 0 2013-09-22 16:13 ollyapi 87 | -rwxrwxrwx 1 0vercl0k 0 2547200 2012-11-18 21:46 ollydbg.exe 88 | -rw-rw-rw- 1 0vercl0k 0 13705 2013-09-22 16:18 ollydbg.ini 89 | drw-rw-rw- 7 0vercl0k 0 4096 2013-09-22 16:13 ollydbg2-plugin-development-files 90 | drw-rw-rw- 2 0vercl0k 0 0 2013-09-22 16:17 plugins 91 | -rw-rw-rw- 1 0vercl0k 0 2713 2013-09-22 16:13 README.md 92 | drw-rw-rw- 11 0vercl0k 0 4096 2013-09-22 16:13 samples 93 | drw-rw-rw- 2 0vercl0k 0 4096 2013-09-22 16:17 udds 94 | ``` 95 | 96 | 3. Build the python-loader project in Release mode and check you have a `python-loader.dll` file in `plugins/`: 97 | 98 | ``` 99 | D:\tmp\ollydbg2-python>ls -la plugins 100 | total 24 101 | drw-rw-rw- 2 0vercl0k 0 0 2013-09-22 16:22 . 102 | drw-rw-rw- 8 0vercl0k 0 4096 2013-09-22 16:17 .. 103 | -rw-rw-rw- 1 0vercl0k 0 18432 2013-09-22 16:22 python-loader.dll 104 | ``` 105 | 106 | 4. Build the python-buildings-swig project in Release mode, check you have a `_python_bindings_swig.pyd` file and a `python_bindings_swig.py` in `ollyapi/`: 107 | 108 | ``` 109 | D:\tmp\ollydbg2-python>ls -la ollyapi 110 | total 1436 111 | drw-rw-rw- 2 0vercl0k 0 4096 2013-09-22 16:26 . 112 | drw-rw-rw- 8 0vercl0k 0 4096 2013-09-22 16:17 .. 113 | [...] 114 | -rw-rw-rw- 1 0vercl0k 0 971776 2013-09-22 14:09 _python_bindings_swig.pyd 115 | -rw-rw-rw- 1 0vercl0k 0 416207 2013-09-22 14:08 python_bindings_swig.py 116 | ``` 117 | 118 | 5. Now launch `ollydbg.exe`, and check the log window to see if the python-loader plugin has been successfully loaded. 119 | 6. Script and have fun! 120 | 121 | 122 | Known Issues 123 | ============ 124 | 125 | If you encounter any issues please let me know by filling an issue here: [https://github.com/0vercl0k/ollydbg2-python/issues](https://github.com/0vercl0k/ollydbg2-python/issues). Also try to be explicit, and give me enough details to be able to repro the issue on my machine: OS version, OllyDbg2 configuration, script, etc. 126 | 127 | 128 | Contributing 129 | ============ 130 | 131 | Feel free to contribute to this project: if you're used to play with Windows' GUI API please help me to make a working bar, if you have idea about cool samples to show case the API send me your ideas, if you want to implement some high level API methods please do! 132 | If you have also any comments, remarks I would love to hear them :). -------------------------------------------------------------------------------- /ollyapi/threads.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # threads.py - High level API to play with threads related stuff. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from python_bindings_swig import * 22 | 23 | # Wrappers 24 | 25 | def ResumeAllThreads(): 26 | Resumeallthreads() 27 | 28 | def ThreadRegisters(threadid): 29 | """ 30 | Get the registers (SEE/FPU/General registers/etc) for the current thread debugged 31 | """ 32 | return Threadregisters(threadid) 33 | 34 | def GetCpuThreadId(): 35 | """ 36 | Get the TID of the current thread 37 | """ 38 | return Getcputhreadid() 39 | 40 | # The metaprogramming trick doesn't work because the ip field isn't in the t_reg.r array :( 41 | def SetEip(eip = 0): 42 | """ 43 | Modify the EIP register 44 | """ 45 | p_reg = Threadregisters(GetCpuThreadId()) 46 | p_reg.ip = eip 47 | 48 | def GetEip(): 49 | """ 50 | Get the EIP register 51 | """ 52 | p_reg = Threadregisters(GetCpuThreadId()) 53 | return p_reg.ip 54 | 55 | def GetEflags(): 56 | """ 57 | Get the EFLAGS register 58 | """ 59 | p_reg = Threadregisters(GetCpuThreadId()) 60 | return p_reg.flags 61 | 62 | def GetProcessId(): 63 | """ 64 | Get the PID of the debuggee 65 | """ 66 | # oddata (ulong) processid; // Process ID of Debuggee or 0 67 | return cvar.processid 68 | 69 | def GetProcessHandle(): 70 | """ 71 | Get the handle on the debuggee (obtained via OpenProcess) 72 | """ 73 | # oddata (HANDLE) process; // Handle of Debuggee or NULL 74 | return cvar.process 75 | 76 | # metaprogramming magixx 77 | 78 | def CreateRegisterSetter(reg_id, reg_name): 79 | """ 80 | Create dynamically a setter function for an x86 register contained in t_reg.r 81 | """ 82 | def template_func(reg_value): 83 | r = Threadregisters(GetCpuThreadId()) 84 | regs = ulongArray.frompointer(r.r) 85 | regs[reg_id] = reg_value 86 | 87 | f = template_func 88 | # adjust correctly the name of the futur function 89 | f.__name__ = 'Set%s' % reg_name.capitalize() 90 | f.__doc__ = 'Set the %s register' % reg_name.upper() 91 | 92 | return (f.__name__, f) 93 | 94 | def CreateRegisterGetter(reg_id, reg_name): 95 | """ 96 | Create dynamically a getter function for an x86 register contained in t_reg.r 97 | """ 98 | def template_func(): 99 | r = Threadregisters(GetCpuThreadId()) 100 | regs = ulongArray.frompointer(r.r) 101 | return regs[reg_id] 102 | 103 | f = template_func 104 | # adjust correctly the name of the futur function 105 | f.__name__ = 'Get%s' % reg_name.capitalize() 106 | f.__doc__ = 'Get the %s register' % reg_name.upper() 107 | 108 | return (f.__name__, f) 109 | 110 | def CreateSegRegisterGetter(seg_id, seg_name): 111 | """ 112 | Create dynamically a getter function for x86 segment selectors contained in t_reg.s 113 | """ 114 | def template_func(): 115 | r = Threadregisters(GetCpuThreadId()) 116 | s = ulongArray.frompointer(r.s) 117 | return s[seg_id] 118 | 119 | f = template_func 120 | # adjust correctly the name of the futur function 121 | f.__name__ = 'Get%s' % seg_name.capitalize() 122 | f.__doc__ = 'Get the %s segment selector' % seg_name.upper() 123 | 124 | return (f.__name__, f) 125 | 126 | 127 | def BuildSettersGetters(): 128 | """ 129 | Create dynamically all the getters/setters function used to retrieve/set register value in 130 | t_reg.r 131 | """ 132 | list_reg = [ 133 | ('eax', REG_EAX), 134 | ('ecx', REG_ECX), 135 | ('edx', REG_EDX), 136 | ('ebx', REG_EBX), 137 | ('esp', REG_ESP), 138 | ('ebp', REG_EBP), 139 | ('esi', REG_ESI), 140 | ('edi', REG_EDI) 141 | ] 142 | 143 | list_seg = [ 144 | ('cs', SEG_CS), 145 | ('ss', SEG_SS), 146 | ('es', SEG_ES), 147 | ('ds', SEG_DS), 148 | ('gs', SEG_GS), 149 | ('fs', SEG_FS) 150 | ] 151 | 152 | for reg_name, reg_id in list_reg: 153 | # Build the setter 154 | n, f = CreateRegisterSetter(reg_id, reg_name) 155 | globals()[n] = f 156 | 157 | # Build the getter 158 | n, f = CreateRegisterGetter(reg_id, reg_name) 159 | globals()[n] = f 160 | 161 | for seg_name, seg_id in list_seg: 162 | n, f = CreateSegRegisterGetter(seg_id, seg_name) 163 | globals()[n] = f 164 | 165 | # it's a bit magic, instanciation of the functions! 166 | BuildSettersGetters() 167 | 168 | 169 | # Abstraction 170 | 171 | def GetCurrentThreadRegisters(): 172 | """ 173 | Retrieve the register for the current thread debugged 174 | """ 175 | current_tid = GetCpuThreadId() 176 | if current_tid == 0: 177 | return None 178 | return ThreadRegisters(current_tid) 179 | 180 | def SetRegisters(r): 181 | """ 182 | Modify several CPU registers at the same time 183 | """ 184 | handlers = { 185 | 'eax' : SetEax, 186 | 'ebx' : SetEbx, 187 | 'ecx' : SetEcx, 188 | 'edx' : SetEdx, 189 | 'esi' : SetEsi, 190 | 'edi' : SetEdi, 191 | 'esp' : SetEsp, 192 | 'ebp' : SetEbp, 193 | 'eip' : SetEip 194 | } 195 | 196 | for reg_name, value in r.iteritems(): 197 | if reg_name in handlers: 198 | handlers[reg_name](value) 199 | 200 | def GetCurrentTEB(): 201 | """ 202 | Retrieve the base of the TEB 203 | """ 204 | r = GetCurrentThreadRegisters() 205 | base = ulongArray.frompointer(r.base) 206 | return base[SEG_FS] 207 | 208 | def display_global_registers(): 209 | """ 210 | Display only the global registers 211 | """ 212 | r = GetCurrentThreadRegisters() 213 | p_reg = ulongArray.frompointer(r.r) 214 | print 'EAX: %#.8x, ECX: %#.8x' % (p_reg[REG_EAX], p_reg[REG_ECX]) 215 | print 'EDX: %#.8x, EBX: %#.8x' % (p_reg[REG_EDX], p_reg[REG_EBX]) 216 | print 'ESP: %#.8x, EBP: %#.8x' % (p_reg[REG_ESP], p_reg[REG_EBP]) 217 | print 'ESI: %#.8x, EDI: %#.8x' % (p_reg[REG_ESI], p_reg[REG_EDI]) 218 | print 'EIP: %#.8x' % r.ip 219 | 220 | def display_segment_selectors(): 221 | """ 222 | Display the segment selectors with their bases/limits 223 | """ 224 | r = GetCurrentThreadRegisters() 225 | s = ulongArray.frompointer(r.s) 226 | base = ulongArray.frompointer(r.base) 227 | limit = ulongArray.frompointer(r.limit) 228 | print 'ES: %#.2x (%#.8x - %#.8x), CS: %#.2x (%#.8x - %#.8x)' % (s[SEG_ES], base[SEG_ES], (base[SEG_ES] + limit[SEG_ES]), s[SEG_CS], base[SEG_CS], (base[SEG_CS] + limit[SEG_CS])) 229 | print 'SS: %#.2x (%#.8x - %#.8x), DS: %#.2x (%#.8x - %#.8x)' % (s[SEG_SS], base[SEG_SS], (base[SEG_SS] + limit[SEG_SS]), s[SEG_DS], base[SEG_DS], (base[SEG_DS] + limit[SEG_DS])) 230 | print 'FS: %#.2x (%#.8x - %#.8x), GS: %#.2x (%#.8x - %#.8x)' % (s[SEG_FS], base[SEG_FS], (base[SEG_FS] + limit[SEG_FS]), s[SEG_GS], base[SEG_GS], (base[SEG_GS] + limit[SEG_GS])) 231 | 232 | def display_eflags(): 233 | """ 234 | Display the EFLAGS 235 | """ 236 | r = GetCurrentThreadRegisters() 237 | 238 | print 'EFLAGS:' 239 | print 'Carry flag : %d' % ((r.flags & FLAG_C) != 0) 240 | print 'Parity flag : %d' % ((r.flags & FLAG_P) != 0) 241 | print 'Auxiliary carry flag : %d' % ((r.flags & FLAG_A) != 0) 242 | print 'Zero flag : %d' % ((r.flags & FLAG_Z) != 0) 243 | print 'Sign flag : %d' % ((r.flags & FLAG_S) != 0) 244 | print 'Single-step trap flag: %d' % ((r.flags & FLAG_T) != 0) 245 | print 'Direction flag : %d' % ((r.flags & FLAG_D) != 0) 246 | print 'Overflow flag : %d' % ((r.flags & FLAG_O) != 0) 247 | 248 | def display_all_registers(): 249 | """ 250 | Display all the CPU registers: global, segment selector, eflags, etc 251 | """ 252 | # global 253 | display_global_registers() 254 | 255 | # Segment selectors 256 | display_segment_selectors() 257 | 258 | # eflags 259 | display_eflags() 260 | -------------------------------------------------------------------------------- /ollyapi/breakpoints.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # breakpoints.py - A python high level API to play with breakpoints in OllyDBG2 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from python_bindings_swig import * 22 | 23 | def SetInt3Breakpoint(address, type_bp = 0, fnindex = 0, limit = 0, count = 0, actions = 0, condition = '', expression = '', exprtype = ''): 24 | """ 25 | Python wrapper for the Setint3breakpoint function 26 | """ 27 | return Setint3breakpoint( 28 | address, 29 | type_bp, 30 | fnindex, 31 | limit, 32 | count, 33 | actions, 34 | condition, 35 | expression, 36 | exprtype 37 | ) 38 | 39 | def SetHardBreakpoint(address, index = 0, size = 0, type_ = 0, fnindex = 0, limit = 0, count = 0, actions = 0, condition = '', expression = '', exprtype = ''): 40 | """ 41 | Set a hardware breakpoint 42 | """ 43 | return Sethardbreakpoint( 44 | index, 45 | size, 46 | type_, 47 | fnindex, 48 | address, 49 | limit, 50 | count, 51 | actions, 52 | condition, 53 | expression, 54 | exprtype 55 | ) 56 | 57 | def RemoveInt3Breakpoint(address, type_bp): 58 | """ 59 | Remove software breakpoint 60 | """ 61 | return Removeint3breakpoint( 62 | address, 63 | type_bp 64 | ) 65 | 66 | def RemoveHardbreapoint(slot): 67 | """ 68 | Remove hardware breakpoint 69 | """ 70 | return Removehardbreakpoint(slot) 71 | 72 | def FindFreeHardbreakSlot(type_): 73 | """ 74 | Find a free slot to put your hardware breakpoint 75 | """ 76 | return Findfreehardbreakslot(type_) 77 | 78 | def SetMemoryBreakpoint(address, size = 1, type_ = 0, limit = 0, count = 0, condition = '', expression = '', exprtype = ''): 79 | """ 80 | Set a memory breakpoint 81 | """ 82 | return Setmembreakpoint( 83 | address, 84 | size, 85 | type_, 86 | limit, 87 | count, 88 | condition, 89 | expression, 90 | exprtype 91 | ) 92 | 93 | def RemoveMemoryBreakpoint(addr): 94 | """ 95 | Remove a memory breakpoint 96 | """ 97 | return Removemembreakpoint(addr) 98 | 99 | 100 | # WE NEED ABSTRACTION MAN 101 | 102 | class Breakpoint(object): 103 | """ 104 | """ 105 | def __init__(self, address, type_bp, condition = None): 106 | self.address = address 107 | self.state = 'Disabled' 108 | self.type = type_bp 109 | self.is_conditional_bp = condition != None 110 | self.condition = '' if condition == None else condition 111 | 112 | def get_address(self): 113 | """Get the address of the breakpoint""" 114 | return self.address 115 | 116 | def get_state(self): 117 | """ 118 | Get the state of your breakpoint 119 | """ 120 | return self.state 121 | 122 | def is_enabled(self): 123 | """ 124 | Is the breakpoint enabled ? 125 | """ 126 | return self.state == 'Enabled' 127 | 128 | def is_disabled(self): 129 | """ 130 | Is the breakpoint disabled ? 131 | """ 132 | return self.state == 'Disabled' 133 | 134 | def disable(self): 135 | """ 136 | Disable the breakpoint 137 | """ 138 | pass 139 | 140 | def remove(self): 141 | """ 142 | Remove the breakpoint 143 | """ 144 | pass 145 | 146 | def enable(self): 147 | """ 148 | Enable the breakpoint 149 | """ 150 | pass 151 | 152 | @staticmethod 153 | def flags_to_bp_type(flags, is_conditional_bp = False): 154 | """ 155 | Translate the 'rwx' in stuff that olly understands 156 | """ 157 | # remove duplicate letter 158 | flags = ''.join(set(flags)) 159 | 160 | # now ensure the maximum you can do is 'rwx' 161 | assert(len(flags) < 4) 162 | 163 | # we want only 'r', 'w' & 'x' in the flags 164 | assert(filter(lambda x: x in ['r', 'w', 'x'], flags) != []) 165 | 166 | # converting the flags into valid breakpoint type 167 | type_dword = BP_MANUAL | BP_BREAK 168 | if 'r' in flags: 169 | type_dword |= BP_READ 170 | 171 | if 'w' in flags: 172 | type_dword |= BP_WRITE 173 | 174 | if 'x' in flags: 175 | type_dword |= BP_EXEC 176 | 177 | if is_conditional_bp != '': 178 | type_dword |= BP_COND 179 | 180 | return type_dword 181 | 182 | 183 | class SoftwareBreakpoint(Breakpoint): 184 | """ 185 | A class to manipulate, play with software breakpoint 186 | 187 | TODO: 188 | - disable 189 | - .continue(x) -> let the breakpoint be hit x times 190 | """ 191 | def __init__(self, address, condition = None): 192 | # if this is a classic breakpoint we need to set different flag 193 | t = BP_MANUAL | ((BP_COND | BP_CONDBREAK) if condition != None else BP_BREAK) 194 | 195 | # init internal state of the breakpoint 196 | super(SoftwareBreakpoint, self).__init__(address, t, condition) 197 | 198 | # enable directly the software breakpoint 199 | self.enable() 200 | 201 | def enable(self): 202 | if self.state != 'Enabled': 203 | 204 | r = SetInt3Breakpoint( 205 | self.address, 206 | self.type, 207 | condition = self.condition 208 | ) 209 | 210 | self.state = 'Enabled' 211 | return r 212 | 213 | def remove(self): 214 | # we remove the breakpoint only if it is enabled 215 | if self.state == 'Enabled': 216 | RemoveInt3Breakpoint(self.address, self.type) 217 | self.state = 'Disabled' 218 | 219 | class HardwareBreakpoint(Breakpoint): 220 | """ 221 | A class to manipulate, play with hardware breakpoint 222 | 223 | Note: 224 | - do not use .goto() if you have a read/write bp, because you don't know where 225 | the breakpoint is going to be hit 226 | """ 227 | def __init__(self, address, flags = 'x', size = 1, condition = None, slot = None): 228 | # init internal state of the breakpoint 229 | super(HardwareBreakpoint, self).__init__(address, flags, condition) 230 | 231 | assert(size in [1, 2, 4]) 232 | 233 | # DR7 allows only 3 types of HWBP: 234 | # -> on execution (00b) 235 | # -> data write (01b) 236 | # -> data read or write (11b) 237 | assert(flags in ['rw', 'w', 'wr', 'x']) 238 | 239 | # ensure the size is 1byte if this is an execution breakpoint 240 | if flags == 'x' : 241 | size = 1 242 | 243 | self.size = size 244 | 245 | # keep in memory the flags view like 'rw' breakpoint, but translate it into something 246 | # ollydbg understands 247 | self.internal_type = Breakpoint.flags_to_bp_type(self.type, self.is_conditional_bp) 248 | 249 | self.slot = slot if slot != None else FindFreeHardbreakSlot(self.internal_type) 250 | if self.slot == -1: 251 | raise Exception('You have used all the available slot') 252 | 253 | self.enable() 254 | 255 | def enable(self): 256 | if self.state != 'Enabled': 257 | r = SetHardBreakpoint( 258 | self.address, 259 | self.slot, 260 | self.size, 261 | self.internal_type, 262 | condition = self.condition 263 | ) 264 | 265 | self.state = 'Enabled' 266 | return r 267 | 268 | def remove(self): 269 | if self.state == 'Enabled': 270 | RemoveHardbreapoint(self.slot) 271 | self.state = 'Disabled' 272 | 273 | class MemoryBreakpoint(Breakpoint): 274 | """ 275 | A class to manipulate memory breakpoints 276 | """ 277 | def __init__(self, address, flags = 'x', size = 1, condition = ''): 278 | super(MemoryBreakpoint, self).__init__(address, flags, condition) 279 | self.internal_type = Breakpoint.flags_to_bp_type(self.type, self.is_conditional_bp) 280 | self.size = size 281 | 282 | self.enable() 283 | 284 | def enable(self): 285 | if self.state != 'Enabled': 286 | r = SetMemoryBreakpoint( 287 | self.address, 288 | self.size, 289 | self.internal_type, 290 | condition = '' 291 | ) 292 | 293 | self.state = 'Enabled' 294 | return r 295 | 296 | def remove(self): 297 | if self.state == 'Enabled': 298 | RemoveMemoryBreakpoint(self.address) 299 | self.state = 'Disabled' 300 | -------------------------------------------------------------------------------- /ollyapi/sym.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # sym.py - High level API to play with the Symbol API 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from python_bindings_swig import * 22 | from ctypes import * 23 | import threads 24 | 25 | # Structures 26 | 27 | MAX_SYM_NAME = 2000 28 | MAX_PATH = 260 29 | 30 | class symbol_info_t(Structure): 31 | """ 32 | Contains symbol information. 33 | Size: 88bytes 34 | """ 35 | _pack_ = 8 36 | _fields_ = [ 37 | # The size of the structure, in bytes. This member must be set to sizeof(SYMBOL_INFO). 38 | # Note that the total size of the data is the SizeOfStruct + (MaxNameLen - 1) * sizeof(TCHAR). 39 | # The reason to subtract one is that the first character in the name is accounted for in the size of the structure. 40 | ('SizeOfStruct', c_ulong), 41 | 42 | # A unique value that identifies the type data that describes the symbol. This value does not persist between sessions. 43 | ('TypeIndex', c_ulong), 44 | 45 | # This member is reserved for system use. 46 | ('Reserved', c_longlong * 2), 47 | 48 | # The unique value for the symbol. The value associated with a symbol is not guaranteed to be the same each time you run the process. 49 | # For PDB symbols, the index value for a symbol is not generated until the symbol is enumerated or retrieved through a search by name or address. 50 | # The index values for all CodeView and COFF symbols are generated when the symbols are loaded. 51 | ('Index', c_ulong), 52 | 53 | # The symbol size, in bytes. 54 | # This value is meaningful only if the module symbols are from a pdb file; otherwise, this value is typically zero and should be ignored. 55 | ('Size', c_ulong), 56 | 57 | # The base address of the module that contains the symbol. 58 | ('ModBase', c_longlong), 59 | 60 | ('Flags', c_ulong), 61 | 62 | # The value of a constant. 63 | ('Value', c_longlong), 64 | 65 | # The virtual address of the start of the symbol. 66 | ('Address', c_longlong), 67 | 68 | # The register. 69 | ('Register', c_ulong), 70 | 71 | # The DIA scope. 72 | # For more information, see the Debug Interface Access SDK in the Visual Studio documentation. 73 | # (This resource may not be available in some languages and countries.) 74 | ('Scope', c_ulong), 75 | 76 | # The PDB classification. 77 | # These values are defined in Dbghelp.h in the SymTagEnum enumeration type. 78 | ('Tag', c_ulong), 79 | 80 | # The length of the name, in characters, not including the null-terminating character. 81 | # addr_array = addressof(struct) + sizeof(ULONG) + sizeof(ULONG) 82 | # s = string_at(addr_array) 83 | ('NameLen', c_ulong), 84 | 85 | # The size of the Name buffer, in characters. 86 | # If this member is 0, the Name member is not used. 87 | ('MaxNameLen', c_ulong), 88 | 89 | # The name of the symbol. 90 | # The name can be undecorated if the SYMOPT_UNDNAME option is used with the SymSetOptions function. 91 | ('Name', c_char * 1) 92 | ] 93 | 94 | symbol_info_p = POINTER(symbol_info_t) 95 | 96 | class guid_t(Structure): 97 | """ 98 | GUIDs identify objects such as interfaces, manager entry-point vectors (EPVs), and class objects. 99 | A GUID is a 128-bit value consisting of one group of 8 hexadecimal digits, 100 | followed by three groups of 4 hexadecimal digits each, followed by one group of 12 hexadecimal digits. 101 | The following example GUID shows the groupings of hexadecimal digits in a GUID: 6B29FC40-CA47-1067-B31D-00DD010662DA 102 | 103 | Size: 16bytes 104 | """ 105 | _pack_ = 4 106 | _fields_ = [ 107 | ('Data1', c_ulong), 108 | ('Data2', c_ushort), 109 | ('Data3', c_ushort), 110 | ('Data4', c_char * 8) 111 | ] 112 | 113 | class imagehlp_module64_t(Structure): 114 | """ 115 | Contains module information. 116 | Size: 1672 117 | """ 118 | _pack_ = 8 119 | _fields_ = [ 120 | # The size of the structure, in bytes. 121 | # The caller must set this member to sizeof(IMAGEHLP_MODULE64). 122 | ('SizeOfStruct', c_ulong), 123 | 124 | # The base virtual address where the image is loaded. 125 | ('BaseOfImage', c_longlong), 126 | 127 | # The size of the image, in bytes. 128 | ('ImageSize', c_ulong), 129 | 130 | # The date and timestamp value. 131 | # The value is represented in the number of seconds elapsed since midnight (00:00:00), January 1, 1970, 132 | # Universal Coordinated Time, according to the system clock. 133 | # The timestamp can be printed using the C run-time (CRT) function ctime. 134 | ('TimeDateStamp', c_ulong), 135 | 136 | # The checksum of the image. 137 | # This value can be zero. 138 | ('CheckSum', c_ulong), 139 | 140 | # The number of symbols in the symbol table. 141 | # The value of this parameter is not meaningful when SymPdb is specified as the value of the SymType parameter. 142 | ('NumSyms', c_ulong), 143 | 144 | # The type of symbols that are loaded. 145 | ('SymType', c_int), 146 | 147 | # The module name. 148 | ('ModuleName', c_char * 32), 149 | 150 | # The image name. The name may or may not contain a full path. 151 | ('ImageName', c_char * 256), 152 | 153 | # The full path and file name of the file from which symbols were loaded. 154 | ('LoadedImageName', c_char * 256), 155 | 156 | # The full path and file name of the .pdb file. 157 | ('LoadedPdbName', c_char * 256), 158 | 159 | # The signature of the CV record in the debug directories. 160 | ('CVSig', c_ulong), 161 | 162 | # The contents of the CV record. 163 | ('CVData', c_char * (MAX_PATH * 3)), 164 | 165 | # The PDB signature. 166 | ('PdbSig', c_ulong), 167 | 168 | # The PDB signature (Visual C/C++ 7.0 and later) 169 | ('PdbSig70', guid_t), 170 | 171 | # The DBI age of PDB. 172 | ('PdbAge', c_ulong), 173 | 174 | # A value that indicates whether the loaded PDB is unmatched. 175 | ('PdbUnmatched', c_long), 176 | 177 | # A value that indicates whether the loaded DBG is unmatched. 178 | ('DbgUnmatched', c_long), 179 | 180 | # A value that indicates whether line number information is available. 181 | ('LineNumbers', c_long), 182 | 183 | # A value that indicates whether symbol information is available. 184 | ('GlobalSymbols', c_long), 185 | 186 | # A value that indicates whether type information is available. 187 | ('TypeInfo', c_long), 188 | 189 | # A value that indicates whether the .pdb supports the source server. 190 | ('SourceIndexed', c_long), 191 | 192 | # A value that indicates whether the module contains public symbols. 193 | ('Publics', c_long), 194 | ] 195 | 196 | imagehlp_module64_p = POINTER(imagehlp_module64_t) 197 | 198 | c_longlong_p = POINTER(c_longlong) 199 | 200 | # Wrapper 201 | 202 | def resolve_api(n, mod): 203 | """ 204 | Retrieve dynamically the function address exported 205 | by OllyDbg 206 | """ 207 | addr = windll.kernel32.GetProcAddress( 208 | windll.kernel32.GetModuleHandleA(mod), 209 | n 210 | ) 211 | 212 | assert(addr != 0) 213 | return addr 214 | 215 | # XXX: ctypes.wintypes doesn't exist in python 2.6 216 | # BOOL WINAPI SymInitialize( 217 | # _In_ HANDLE hProcess, 218 | # _In_opt_ PCTSTR UserSearchPath, 219 | # _In_ BOOL fInvadeProcess 220 | # ); 221 | 222 | # In [13]: wintypes.BOOL 223 | # Out[13]: ctypes.c_long 224 | # In [14]: wintypes.HANDLE 225 | # Out[14]: ctypes.c_void_p 226 | # In [15]: wintypes.LPCSTR 227 | # Out[15]: ctypes.c_char_p 228 | Syminitialize_TYPE = WINFUNCTYPE(c_long, c_void_p, c_char_p, c_long) 229 | Syminitialize = Syminitialize_TYPE(resolve_api('SymInitialize', 'dbghelp.dll')) 230 | 231 | # BOOL WINAPI SymFromAddr( 232 | # _In_ HANDLE hProcess, 233 | # _In_ DWORD64 Address, 234 | # _Out_opt_ PDWORD64 Displacement, 235 | # _Inout_ PSYMBOL_INFO Symbol 236 | # ); 237 | Symfromaddr_TYPE = WINFUNCTYPE(c_bool, c_void_p, c_longlong, c_longlong_p, symbol_info_p) 238 | Symfromaddr = Symfromaddr_TYPE(resolve_api('SymFromAddr', 'dbghelp.dll')) 239 | 240 | # BOOL WINAPI SymGetModuleInfo64( 241 | # _In_ HANDLE hProcess, 242 | # _In_ DWORD64 dwAddr, 243 | # _Out_ PIMAGEHLP_MODULE64 ModuleInfo 244 | # ); 245 | Symgetmoduleinfo64_TYPE = WINFUNCTYPE(c_bool, c_void_p, c_longlong, imagehlp_module64_p) 246 | Symgetmoduleinfo64 = Symgetmoduleinfo64_TYPE(resolve_api('SymGetModuleInfo64', 'dbghelp.dll')) 247 | 248 | def SymInitialize(hProcess, UserSearchPath = None, fInvadeProcess = True): 249 | """ 250 | Initializes the symbol handler for a process. 251 | 252 | Note: OllyDBG seems to call this function, thus you shouldn't call it. 253 | """ 254 | return Syminitialize( 255 | hProcess, 256 | UserSearchPath, 257 | fInvadeProcess 258 | ) 259 | 260 | def SymFromAddr(hProcess, address): 261 | """ 262 | Retrieves symbol information for the specified address. 263 | """ 264 | displacement = c_longlong(0) 265 | 266 | # A pointer to a SYMBOL_INFO structure that provides information about the symbol. 267 | # The symbol name is variable in length; therefore this buffer must be large enough to hold the name stored at the end of the SYMBOL_INFO structure. 268 | # Be sure to set the MaxNameLen member to the number of bytes reserved for the name. 269 | buf = create_string_buffer(sizeof(symbol_info_t) + (MAX_SYM_NAME * sizeof(c_char))) 270 | p_symbol = cast(buf, symbol_info_p) 271 | p_symbol.contents.SizeOfStruct = c_ulong(sizeof(symbol_info_t)) 272 | p_symbol.contents.MaxNameLen = MAX_SYM_NAME 273 | 274 | r = Symfromaddr( 275 | c_void_p(hProcess), 276 | c_longlong(address), 277 | c_longlong_p(displacement), 278 | p_symbol 279 | ) 280 | 281 | if r == False: 282 | return None 283 | 284 | # -4 because of the pad appended after our structure 285 | addr_s = addressof(buf) + sizeof(symbol_info_t) - 4 286 | s = string_at(addr_s) 287 | 288 | return { 289 | 'struct' : p_symbol.contents, 290 | 's' : s, 291 | 'displacement' : displacement 292 | } 293 | 294 | def SymGetModuleInfo64(hProcess, address): 295 | """ 296 | Retrieves the module information of the specified module. 297 | """ 298 | 299 | img = imagehlp_module64_t() 300 | img.SizeOfStruct = c_ulong(sizeof(imagehlp_module64_t)) 301 | 302 | r = Symgetmoduleinfo64( 303 | c_void_p(hProcess), 304 | c_longlong(address), 305 | imagehlp_module64_p(img) 306 | ) 307 | 308 | if r == 0: 309 | return None 310 | 311 | return img 312 | 313 | def DecodeAddress(addr): 314 | """ 315 | Obtain symbol from OllyDBG via an address 316 | 317 | Note: It works only if address is the *exact* address of the function 318 | I mean if you're trying to get the symbol of function+1, it won't retrieve anything 319 | In this case you have to use DecodeRelativeOffset 320 | 321 | Example: 322 | DecodeAddress(0x31337) = binary.function 323 | """ 324 | buf = bytearray(256) 325 | r = Decodeaddress( 326 | addr, 327 | 0, 328 | 0x20400, 329 | buf, 330 | 256, 331 | None 332 | ) 333 | 334 | if r <= 0: 335 | return None 336 | 337 | return str(buf.replace('\x00', '')) 338 | 339 | def DecodeRelativeOffset(addr): 340 | """ 341 | """ 342 | buf = bytearray(256) 343 | r = Decoderelativeoffset( 344 | addr, 345 | 0x20400, 346 | buf, 347 | 100 348 | ) 349 | 350 | if r <= 0: 351 | return None 352 | 353 | return str(buf.replace('\x00', '')) 354 | 355 | # Abstraction 356 | 357 | 358 | def GetSymbolFromAddressMS(address): 359 | """ 360 | Retrieve symbol information from an address via the Windows Symbol API 361 | 362 | Example: 363 | GetSymbolFromAddressMS(0x778de752) = ntdll!RtlAnsiStringToUnicodeString+0x0000007d 364 | """ 365 | handle_process = threads.GetProcessHandle() 366 | address_info = SymFromAddr(handle_process, address) 367 | s = None 368 | 369 | if address_info != None: 370 | symbol_name, offset = address_info['s'], address_info['displacement'].value 371 | module_info = SymGetModuleInfo64(handle_process, address) 372 | 373 | if module_info != None: 374 | s = '%s.%s+%#.8x' % (module_info.ModuleName, symbol_name, offset) 375 | else: 376 | s = '%s+%#.8x' % (symbol_name, offset) 377 | 378 | return s 379 | 380 | def GetSymbolFromAddressOlly(address): 381 | """ 382 | Retrieve symbol information from an address via the OllyDBG API 383 | 384 | Example: 385 | GetSymbolFromAddressOlly(0x778de752) = 386 | """ 387 | s = DecodeRelativeOffset(address) 388 | if s == None: 389 | s = DecodeAddress(address) 390 | 391 | return s 392 | 393 | def GetSymbolFromAddress(address): 394 | """ 395 | Try to obtain a symbol via, first the MS API, 396 | and if it didn't succeed via the OllyDBG API 397 | """ 398 | s = GetSymbolFromAddressMS(address) 399 | if s == None: 400 | s = GetSymbolFromAddressOlly(address) 401 | 402 | return s 403 | -------------------------------------------------------------------------------- /ollyapi/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # utils.py - High level API for the utils functions. 6 | # Copyright (C) 2012 Axel "0vercl0k" Souchet - http://www.twitter.com/0vercl0k 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from python_bindings_swig import * 22 | import threads 23 | import memory 24 | import sym 25 | 26 | # Wrappers 27 | 28 | def InsertNameW(addr, type_, s): 29 | """ 30 | That function is used to add label and comment directly on the disassembly 31 | (like with the shortcut ':' or ';') 32 | """ 33 | return InsertnameW( 34 | addr, 35 | type_, 36 | s 37 | ) 38 | 39 | def CheckForDebugEvent(): 40 | """ 41 | Hum, this method seems to be very important, one of its purpose 42 | is to updated the thread registers retrieved thanks to Threadregisters() 43 | """ 44 | return Checkfordebugevent() 45 | 46 | def CloseProcess(confirm = 0): 47 | """ 48 | Close the process being debugged 49 | """ 50 | return Closeprocess(confirm) 51 | 52 | def SetArguments(s): 53 | """ 54 | Set the cmdline passed to the debuggee, exactly the same when you do "File > Set new arguments" 55 | """ 56 | assert(len(s) * 2 < ARGLEN) 57 | cvar.arguments = unicode(s) 58 | 59 | def Disasm_(c, address = 0): 60 | """ 61 | Disassemble some x86 code (only one instruction though) thanks to the OllyDbg2 engine 62 | """ 63 | buff = bytearray(c) 64 | di = t_disasm() 65 | size_instr = Disasm( 66 | buff, 67 | len(buff), 68 | address, 69 | None, 70 | di, 71 | DA_TEXT | DA_OPCOMM | DA_DUMP | DA_MEMORY, 72 | t_reg(), 73 | t_predict() 74 | ) 75 | 76 | return size_instr, str(di.result) 77 | 78 | def Assemble_(s, address = 0): 79 | """ 80 | Assemble some x86 stuff 81 | """ 82 | 83 | # the longuest x86 instruction is 15 bytes long 84 | code = bytearray(15) 85 | 86 | # XXX: it should be enough (?) 87 | error_msg = bytearray(256) 88 | 89 | sizeof_assembled = Assemble( 90 | s, 91 | address, 92 | code, 93 | len(code), 94 | 1, 95 | error_msg 96 | ) 97 | 98 | # you submit invalid x86 assembly 99 | # XXX: doesn't it exist a proper way to do that ?? 100 | if error_msg.startswith('U\x00n\x00k\x00n\x00o\x00w\x00n'): 101 | return (0, 0, str(error_msg.replace('\x00', ''))) 102 | 103 | return str(code[:sizeof_assembled]), sizeof_assembled 104 | 105 | def AssembleAllForms(s, ip = 0): 106 | """ 107 | Actually, I only use this function to obtain a t_asmod structure, to pass it 108 | at CompareCommand() 109 | """ 110 | maxmodel = 0x80 111 | asmod = t_asmmodArray(maxmodel) 112 | 113 | # XXX: it should be enough (?) 114 | error_msg = bytearray(256) 115 | 116 | r = Assembleallforms( 117 | s, 118 | ip, 119 | asmod.cast(), 120 | maxmodel, 121 | 7, 122 | error_msg 123 | ) 124 | 125 | if r == 0: 126 | raise Exception('Cannot assembled your instruction: %s' % str(error_msg.replace('\x00', ''))) 127 | 128 | return asmod, r 129 | 130 | def CompareCommand(cmd, cmdsize, cmdip, model, nmodel): 131 | """ 132 | Compare command, used to search instruction accross the memory 133 | """ 134 | a, b = 0, 0 135 | 136 | return Comparecommand( 137 | cmd, 138 | cmdsize, 139 | cmdip, 140 | model.cast(), 141 | nmodel, 142 | a, 143 | b, 144 | t_disasm() 145 | ) 146 | 147 | def GetAnalyserComment(addr): 148 | """ 149 | Get an analyser comment with an address 150 | 151 | Example of comment you can retrieved: 152 | 153 | """ 154 | buf = bytearray(256) 155 | r = Getanalysercomment( 156 | None, 157 | addr, 158 | buf, 159 | 256 160 | ) 161 | 162 | return str(buf.replace('\x00', '')) 163 | 164 | def GetProcComment(addr, acall = 0, argonly = 0): 165 | """ 166 | Get comment generated for a specific Procedure 167 | 168 | Example of comment: 169 | 004017A0 /$ 55 PUSH EBP ; breakpoints.004017A0(guessed void) 170 | """ 171 | buf = bytearray(256) 172 | r = Getproccomment( 173 | addr, 174 | acall, 175 | buf, 176 | 256, 177 | argonly 178 | ) 179 | 180 | return str(buf.replace('\x00', '')) 181 | 182 | def IsDebuggeeFinished(): 183 | """Is the debugee is finished ?""" 184 | return cvar.run.status == STAT_FINISHED 185 | 186 | 187 | # Abstraction 188 | 189 | def Run__(status = STAT_RUNNING, pass_exception = 0): 190 | """ 191 | Run the process, step-in, step-over, whatever 192 | """ 193 | Run(status, pass_exception) 194 | 195 | # required in order to update the state of the thread registers (retrieved with Threadregisters for example) 196 | # BTW, not sure it's supposed to be done this way though, I've found that in an OllyDBG2 reverse-engineering session. 197 | while CheckForDebugEvent() == 1: 198 | memory.FlushMemoryCache() 199 | 200 | def FindMainModule(): 201 | """ 202 | Get a cool structure filled with juicy information concerning 203 | the process being debugged ; you can find its ImageBase, real ImageBase, etc. 204 | Check t_module structure definition 205 | """ 206 | r = Findmainmodule() 207 | 208 | if r is None: 209 | raise Exception("You haven't loaded any executable file") 210 | 211 | return r 212 | 213 | def AddUserComment(address, s): 214 | """ 215 | Add a user comment at a specific address ; it's like using the shortcut ';' 216 | """ 217 | return InsertNameW( 218 | address, 219 | NM_COMMENT, 220 | s 221 | ) 222 | 223 | def AddUserLabel(address, s): 224 | """ 225 | Add a user label at a specific address ; it's like using the shortcut ':' 226 | """ 227 | return InsertNameW( 228 | address, 229 | NM_LABEL, 230 | s 231 | ) 232 | 233 | def GetPESections(): 234 | """ 235 | Get all the PE sections of your executable 236 | Each entry is a t_secthdr 237 | """ 238 | mod = FindMainModule() 239 | sects = t_secthdrArray.frompointer(mod.sect) 240 | sections = [] 241 | for i in range(mod.nsect): 242 | sections.append(sects[i]) 243 | return sections 244 | 245 | def GetEntryPoint(): 246 | """ 247 | Get the address of the entry point of your executable 248 | """ 249 | mod = FindMainModule() 250 | return mod.entry 251 | 252 | def StepInto(): 253 | """ 254 | Step-into, exactly the same when you hit F7 255 | """ 256 | Run__(STAT_STEPIN) 257 | 258 | def StepOver(): 259 | """ 260 | Step-over, exactly the same when you hit F8 261 | """ 262 | Run__(STAT_STEPOVER) 263 | 264 | def ExecuteUntilRet(): 265 | """ 266 | Execute until RET instruction, exactly the same when you hit ctrl+F9 267 | """ 268 | Run__(STAT_TILLRET) 269 | 270 | def Disass(c, address = 0): 271 | """ 272 | A high level version of the disass function ; this one is able to disassemble 273 | more than one instruction. 274 | """ 275 | sizeof_disassembled_stuff = 0 276 | complete_disass = [] 277 | sizeof_to_disass = len(c) 278 | 279 | while sizeof_disassembled_stuff != sizeof_to_disass: 280 | size_current_instruction, disass = Disasm_(c, address + sizeof_disassembled_stuff) 281 | 282 | # don't want unicode string 283 | disass = str(disass) 284 | 285 | # The engine didn't find a valid x86 code 286 | if disass == '???': 287 | raise Exception("You have submitted stuff OllyDBG doesn't know how to disassemble") 288 | 289 | # In the other case, we have valid assembly code 290 | complete_disass.append({ 291 | 'text' : disass, 292 | 'size' : size_current_instruction 293 | }) 294 | 295 | sizeof_disassembled_stuff += size_current_instruction 296 | c = c[size_current_instruction:] 297 | 298 | return complete_disass 299 | 300 | def Assemble__(s, address = 0): 301 | """ 302 | A high level version of the assemble version ; you can assemble several instructions 303 | each instruction must be separated by a ';' 304 | 305 | Example: mov eax, 0xdeadbeef ; add eax, 4 306 | """ 307 | instrs = s.split(';') 308 | total_size = 0 309 | code = '' 310 | 311 | for instr in instrs: 312 | assem, size = Assemble_(instr, address + len(code)) 313 | 314 | # your instruction doesn't seem possible to assemble 315 | if size == 0: 316 | raise Exception("OllyDBG doesn't know how to assemble this instruction: %s" % s) 317 | 318 | total_size += size 319 | code += assem 320 | 321 | return (code, total_size) 322 | 323 | def FindInstr(instr, address_start = None): 324 | """ 325 | Find the address of a specific instruction 326 | """ 327 | if address_start == None: 328 | address_start = threads.GetEip() 329 | 330 | if memory.IsMemoryExists(address_start) == False: 331 | return 0 332 | 333 | # now assembleallforms to get the t_asmmod required to call comparecommand 334 | asmmod, nmodel = '', 0 335 | try: 336 | #XXX: fix the ip parameter to be able of finding eip-dependent instruction 337 | asmmod, nmodel = AssembleAllForms(instr, 0) 338 | except Exception, e: 339 | raise(e) 340 | 341 | # get information about the memory block 342 | mem_info = memory.FindMemory(address_start) 343 | 344 | # now we can call comparecommand 345 | size_to_dump = mem_info.size - (address_start - mem_info.base) 346 | offset, found = 0, False 347 | 348 | while offset < size_to_dump and found == False: 349 | # XXX: maybe its more efficient to read the whole memory block to avoid ReadMemory calls ? 350 | size_to_read = 16 351 | 352 | # we'll go outside of the boundaries 353 | if (offset + size_to_read) >= size_to_dump: 354 | size_to_read = size_to_dump - offset 355 | 356 | code_process = memory.ReadMemory(size_to_read, address_start + offset) 357 | 358 | r = CompareCommand( 359 | code_process, 360 | size_to_read, 361 | address_start + offset, 362 | asmmod, 363 | nmodel 364 | ) 365 | 366 | # if this is the same command, we found a match \o/ 367 | if r > 0: 368 | found = True 369 | else: 370 | offset += 1 371 | 372 | if found: 373 | return address_start + offset 374 | 375 | return 0 376 | 377 | def FindHexInPage(s, address_start = None): 378 | """ 379 | Find hexadecimal values like E9??FF?A?? 380 | The '?' is a wildcard for one nibbles ; that idea comes from the excellent ODBG scripting language 381 | 382 | Note: This function try to find an hexadecimal pattern only in one page of the memory (either in address_start's page 383 | or in EIP's page) 384 | """ 385 | 386 | def hex_matched(data, s): 387 | """ 388 | Validate if s match with data 389 | """ 390 | does_it_matched = True 391 | idx_data = 0 392 | 393 | for idx in range(0, len(s), 2): 394 | b_str = s[idx : idx+2] 395 | byte_to_compare = ord(data[idx_data]) 396 | 397 | # have we a wildcard ? 398 | if '?' in b_str: 399 | 400 | # wildcard on the high nibble 401 | if b_str[0] == '?' and b_str[1] != '?': 402 | low_nibble = (byte_to_compare & 0x0f) 403 | if low_nibble != int(b_str[1], 16): 404 | does_it_matched = False 405 | 406 | # wildcard on the low nibble 407 | elif b_str[1] == '?' and b_str[0] != '?': 408 | high_nibble = ((byte_to_compare & 0xf0) >> 4) 409 | if high_nibble != int(b_str[0], 16): 410 | does_it_matched = False 411 | # wildcard on the entire byte 412 | else: 413 | pass 414 | 415 | else: 416 | b = int(b_str, 16) 417 | if b != byte_to_compare: 418 | does_it_matched = False 419 | 420 | idx_data += 1 421 | if does_it_matched == False: 422 | break 423 | 424 | return does_it_matched 425 | 426 | # ensure we have a multiple of 2 digits 427 | assert(len(s) % 2 == 0) 428 | s = s.lower() 429 | 430 | # we only accept hexa digits and the wildcard '?' 431 | assert(filter(lambda c: c in '0123456789abcdef?', s) == s) 432 | 433 | if address_start == None: 434 | address_start = threads.GetEip() 435 | 436 | # some memory must be mapped at this address 437 | if memory.IsMemoryExists(address_start) == False: 438 | return 0 439 | 440 | # get information about the memory block 441 | mem_info = memory.FindMemory(address_start) 442 | 443 | size_mem_block = mem_info.size - (address_start - mem_info.base) 444 | offset, found = 0, False 445 | nb_bytes = len(s) / 2 446 | 447 | while offset < (size_mem_block - nb_bytes) and found == False: 448 | data = memory.ReadMemory(nb_bytes, address_start + offset) 449 | if hex_matched(data, s): 450 | found = True 451 | else: 452 | offset += 1 453 | 454 | if found: 455 | return address_start + offset 456 | 457 | return 0 458 | 459 | def display_call_stack(nb_max_frame = 100): 460 | """ 461 | Walk on the stack & generate a call stack 462 | """ 463 | frames_info = [] 464 | args = [] 465 | ebp = threads.GetEbp() 466 | 467 | for i in range(nb_max_frame): 468 | # IsMemoryExists recognizes kernel memory, so we have to manually check it 469 | if memory.IsMemoryExists(ebp) == False or ebp >= 0x80000000: 470 | break 471 | 472 | # at EBP we have the SEBP 473 | sebp = memory.ReadDwordMemory(ebp) 474 | # and right after the SEIP 475 | seip = memory.ReadDwordMemory(ebp + 4) 476 | 477 | if sebp == 0 or seip == 0 or memory.IsMemoryExists(sebp) == False or memory.IsMemoryExists(seip) == False: 478 | break 479 | 480 | symbol = sym.GetSymbolFromAddress(seip) 481 | frames_info.append({ 482 | 'return-address' : seip, 483 | 'address' : sebp + 4, 484 | 'symbol' : symbol if symbol != None else 'no symbol found', 485 | }) 486 | 487 | ebp = sebp 488 | 489 | eip = threads.GetEip() 490 | print "#%.2d %#.8x : %s" % (len(frames_info), eip, sym.GetSymbolFromAddress(eip)) 491 | 492 | for i in range(len(frames_info)): 493 | c = frames_info[i] 494 | ri = len(frames_info) - i - 1 495 | print '#%.2d %#.8x : %s (found @%#.8x)' % (ri, c['return-address'], c['symbol'], c['address']) 496 | 497 | def display_seh_chain(): 498 | """ 499 | Walk on the stack to find the SEH handlers 500 | """ 501 | addr_teb = threads.GetCurrentTEB() 502 | seh_addr = memory.ReadDwordMemory(addr_teb) 503 | if seh_addr == 0: 504 | return None 505 | 506 | seh_entries = [] 507 | 508 | # This is the last entry if SEH.Next = -1 509 | while seh_addr != 0xffffffff: 510 | if memory.IsMemoryExists(seh_addr) == False: 511 | break 512 | 513 | seh_next, seh_handler = memory.ReadDwordMemory(seh_addr), memory.ReadDwordMemory(seh_addr + 4) 514 | 515 | seh_entries.append({ 516 | 'handler' : seh_handler, 517 | 'symbol' : sym.GetSymbolFromAddress(seh_handler), 518 | 'next' : seh_next 519 | }) 520 | 521 | seh_addr = seh_next 522 | 523 | i = 0 524 | for entry in seh_entries: 525 | print '#%.2d - Handler: %s (%#.8x) - Next @ %#.8x' % (i, entry['symbol'], entry['handler'], entry['next']) 526 | i += 1 527 | --------------------------------------------------------------------------------