首页
社区
课程
招聘
[原创]进程内存分析操作工具 - kar98k
发表于: 2019-10-9 16:22 5082

[原创]进程内存分析操作工具 - kar98k

2019-10-9 16:22
5082

工具概述

在分析程序过程中发现部分场景直接调试器附加分析没必要 就想着写款自动化工具尽可能在分析(尤其是前期)过程中解决某些重复步骤
然后就选择脚本语言,在js、lua、python等其它不常见脚本(chaiscript、cling...)中最后选择了python 原因是支持库多

工具特性

  1. 支持内存读写(当然可以dump内存 如果你愿意还可以写个自定义PythonCE
  2. 支持模块注入
  3. 支持执行shellcode
  4. 支持32、64
  5. 可同时操作多个进程互不影响
  6. python2、3支持 (基于python-2716python-374

函数声明及作用:

__init__(...)
    __init__(self: kar98k.kar98k, arg0: str) -> None
    c++签名:kar98k(const std::wstring targetProcessName){...}
    作用: 传入目标进程名称,实例化操作目标进程对象

execute_shell_code(...)
    execute_shell_code(self: kar98k.kar98k, arg0: bytes) -> None
    c++签名:std::string get_decoding_cpp_name(const std::string & funName){...}
    作用: 执行shellcode

get_binmem_by_module(...)
    get_binmem_by_module(self: kar98k.kar98k, arg0: str) -> bytes
    c++签名:py::bytes get_binmem_by_module(std::wstring MDName){...}
    作用: 获取目标模块内存(实时

get_binmem_by_region(...)
    get_binmem_by_region(self: kar98k.kar98k, arg0: int, arg1: int) -> bytes
    c++签名:py::bytes get_binmem_by_region(size_t nAddress , size_t nSize){...}
    作用: 获取指定内存区域内存(实时

get_decoding_cpp_name(...)
    get_decoding_cpp_name(self: kar98k.kar98k, arg0: str) -> str
    c++签名:std::string get_decoding_cpp_name(const std::string & funName){...}
    作用: 获取未粉碎c++函数签名

get_mem_info(...)
    get_mem_info(self: kar98k.kar98k) -> std::vector<kar98k::_MEM_INFO,std::allocator<kar98k::_MEM_INFO> >
    c++签名:std::vector<kar98k::MEMORY_INFO> *get_mem_info(){...}
    作用: 获取进程内存页面信息 返回vector 
    MEMORY_INFO 结构:
    typedef struct _MODULE_INFO {
        MODULEENTRY32W tagModEntry; // 参考 https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/ns-tlhelp32-moduleentry32?redirectedfrom=MSDN
        std::string modName;
        std::string modPath;
    }MODULE_INFO , *PMODULE_INFO;
    --
get_mem_info_size(...)
    get_mem_info_size(self: kar98k.kar98k) -> int
    c++签名:size_t get_mem_info_size(){...}
    作用: 返回保存内存页面信息vector大小

get_mod_info(...)
    get_mod_info(self: kar98k.kar98k) -> std::vector<kar98k::_MODULE_INFO,std::allocator<kar98k::_MODULE_INFO> >
    c++签名:std::vector<kar98k::MODULE_INFO> *get_mod_info(){...}
    作用: 获取进程模块信息 返回vector
    MODULE_INFO 结构:
    typedef struct _MEM_INFO {
        MEMORY_BASIC_INFORMATION memBasicInfo; //参考 https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-memory_basic_information?redirectedfrom=MSDN
        std::string memInModPath;
    }MEMORY_INFO , *PMEM_INFO;

get_mod_info_size(...)
    get_mod_info_size(self: kar98k.kar98k) -> int
    c++签名:size_t get_mod_info_size(){...}
    作用: 返回保存模块信息vector大小

get_tpmem_by_module(...)
    get_tpmem_by_module(self: kar98k.kar98k, arg0: str, arg1: int) -> tuple
    c++签名:py::tuple get_tpmem_by_module(std::wstring MDName , int nBitType){...}
    作用: 以元组形式返回指定模块内存值 (nBitType:1、2、4、8分别对应:8位、16位、32位、64位整形值)

get_tpmem_by_region(...)
    get_tpmem_by_region(self: kar98k.kar98k, arg0: int, arg1: int, arg2: int) -> tuple
    c++签名:py::tuple get_tpmem_by_region(size_t nAddress , size_t nSize , int nBitType){...}
    作用: 以元组形式返回指定内存区域内存值 (nBitType:1、2、4、8分别对应:8位、16位、32位、64位整形值)

get_uint16(...)
    get_uint16(self: kar98k.kar98k, arg0: int) -> int
    c++签名:unsigned __int16 get_uint16(size_t nAddress){...}
    作用: 读取指定地址中的16位整形值

get_uint32(...)
    get_uint32(self: kar98k.kar98k, arg0: int) -> int
    c++签名:unsigned __int32 get_uint32(size_t nAddress){...}
    作用: 读取指定地址中的32位整形值

get_uint64(...)
    get_uint64(self: kar98k.kar98k, arg0: int) -> int
    c++签名:unsigned __int64 get_uint64(size_t nAddress){...}
    作用: 读取指定地址中的64位整形值

get_uint8(...)
    get_uint8(self: kar98k.kar98k, arg0: int) -> int
    c++签名:unsigned __int8 get_uint8(size_t nAddress){...}
    作用: 读取指定地址中的8位整形值

inject_dll(...)
    inject_dll(self: kar98k.kar98k, arg0: str) -> None
    c++签名:void inject_dll(const wchar_t* fullDllPath){...}
    作用: 注入模块

write_buffer(...)
    write_buffer(self: kar98k.kar98k, arg0: int, arg1: bytes) -> None
    c++签名:void write_buffer(size_t nAddress , py::bytes pyBytes){...}
    作用: 在指定地址写入一整块数据

write_uint16(...)
    write_uint16(self: kar98k.kar98k, arg0: int, arg1: int) -> None
    c++签名:void write_uint16(size_t nAddress , unsigned __int16 nValue){...}
    作用: 在指定地址写入16位整形值

write_uint32(...)
    write_uint32(self: kar98k.kar98k, arg0: int, arg1: int) -> None
    c++签名:void write_uint32(size_t nAddress , unsigned __int32 nValue){...}
    作用: 在指定地址写入32位整形值

write_uint64(...)
    write_uint64(self: kar98k.kar98k, arg0: int, arg1: int) -> None
    c++签名:void write_uint64(size_t nAddress , unsigned __int64 nValue){...}
    作用: 在指定地址写入64位整形值

write_uint8(...)
    write_uint8(self: kar98k.kar98k, arg0: int, arg1: int) -> None
    c++签名:void write_uint8(size_t nAddress , unsigned __int8 nValue){...}
    作用: 在指定地址写入8位整形值

get_expfun_info(...)
    get_expfun_info(self: kar98k.kar98k) -> std::vector<kar98k::_FUN_INFO,std::allocator<kar98k::_FUN_INFO> >
    c++签名:std::vector<FUN_INFO>* get_expfun_info(){...}
    作用:获取进程所有模块导出表信息

get_expfun_info_size(...)
    get_expfun_info_size(self: kar98k.kar98k) -> int
    c++签名:size_t get_expfun_info_size(){...}
    作用:获取保存导出表信息vector大小

refresh_expfun_info(...)
    refresh_expfun_info(self: kar98k.kar98k) -> None
    c++签名:void refresh_expfun_info(){...}
    作用:刷新导出表信息

refresh_mem_info(...)
    refresh_mem_info(self: kar98k.kar98k) -> None
    c++签名:void refresh_mem_info(){...}
    作用:刷新进程内存信息

refresh_mod_info(...)
    refresh_mod_info(self: kar98k.kar98k) -> None
    c++签名:void refresh_mod_info(){...}
    作用:刷新进程模块信息

功能演示

image

测试示例

test.py

# coding=utf-8
# 请以管理员启动
import kar98k
from capstone import *



PAGE_NOACCESS     = 0x01    
PAGE_READONLY     = 0x02    
PAGE_READWRITE    = 0x04    
PAGE_WRITECOPY    = 0x08    
PAGE_EXECUTE      = 0x10    
PAGE_EXECUTE_READ = 0x20    
PAGE_EXECUTE_READWRITE = 0x40    
PAGE_EXECUTE_WRITECOPY = 0x80    
PAGE_GUARD        = 0x100    
PAGE_NOCACHE      = 0x200    
PAGE_WRITECOMBINE = 0x400    
PAGE_ENCLAVE_THREAD_CONTROL = 0x80000000  
PAGE_REVERT_TO_FILE_MAP     = 0x80000000  
PAGE_TARGETS_NO_UPDATE      = 0x40000000  
PAGE_TARGETS_INVALID        = 0x40000000  
PAGE_ENCLAVE_UNVALIDATED    = 0x20000000  
PAGE_ENCLAVE_DECOMMIT       = 0x10000000  
MEM_COMMIT                     = 0x00001000  
MEM_RESERVE                    = 0x00002000  
MEM_REPLACE_PLACEHOLDER        = 0x00004000  
MEM_RESERVE_PLACEHOLDER        = 0x00040000  
MEM_RESET                      = 0x00080000  
MEM_TOP_DOWN                   = 0x00100000  
MEM_WRITE_WATCH                = 0x00200000  
MEM_PHYSICAL                   = 0x00400000  
MEM_ROTATE                     = 0x00800000  
MEM_DIFFERENT_IMAGE_BASE_OK    = 0x00800000  
MEM_RESET_UNDO                 = 0x01000000  
MEM_LARGE_PAGES                = 0x20000000  
MEM_4MB_PAGES                  = 0x80000000  
MEM_64K_PAGES                  = (MEM_LARGE_PAGES | MEM_PHYSICAL)  
MEM_UNMAP_WITH_TRANSIENT_BOOST = 0x00000001  
MEM_COALESCE_PLACEHOLDERS      = 0x00000001  
MEM_PRESERVE_PLACEHOLDER       = 0x00000002  
MEM_DECOMMIT                   = 0x00004000  
MEM_RELEASE                    = 0x00008000  
MEM_FREE                       = 0x00010000  
MEM_PRIVATE = 0x00020000  
MEM_MAPPED  = 0x00040000  
MEM_IMAGE   = 0x01000000  





class Test:
    def __init__(self, procName, modName):
        self.procName = procName
        self.modName = modName
        self.user32BaseAddr = 0
        self.objProcess = kar98k.kar98k(procName)

    def __del__(self):
        print('python 对象<%s> 已释放...' %self.procName)


    def test_get_mem(self):
        nAddress = self.user32BaseAddr + 0x1000
        value1 = self.objProcess.get_uint8(nAddress)
        value2= self.objProcess.get_uint16(nAddress)
        value3 = self.objProcess.get_uint32(nAddress)
        value4 = self.objProcess.get_uint64(nAddress)
        value4 = self.objProcess.get_uint64(1)
        value4 = self.objProcess.get_uint64(0)
        tMem1 = self.objProcess.get_tpmem_by_region(nAddress,0x1000,1)
        tMem2 = self.objProcess.get_tpmem_by_region(nAddress,0x1000,2)
        tMem4 = self.objProcess.get_tpmem_by_region(nAddress,0x1000,4)
        tMem8 = self.objProcess.get_tpmem_by_region(nAddress,0x1000,8)
        tMem8 = self.objProcess.get_tpmem_by_region(1,0,8)
        tMem8 = self.objProcess.get_tpmem_by_region(0,0,8)
        tModMem1 = self.objProcess.get_tpmem_by_module("ntdll.dll",1)
        tModMem2 = self.objProcess.get_tpmem_by_module("ntdll.dll",2)
        tModMem4 = self.objProcess.get_tpmem_by_module("ntdll.dll",4)
        tModMem8 = self.objProcess.get_tpmem_by_module("ntdll.dll",8)
        tModMem8 = self.objProcess.get_tpmem_by_module("ntd.dll",8)

    def test_write_mem(self):
        nAddress = self.user32BaseAddr + 0x1000
        self.objProcess.write_uint8(nAddress, 0x11)
        self.objProcess.write_uint16(nAddress, 0x1122)
        self.objProcess.write_uint32(nAddress, 0x11223344)
        self.objProcess.write_uint64(nAddress, 0x1122334455667788)
        binBuffer = b'\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xff'
        self.objProcess.write_buffer(nAddress,binBuffer)


    def test_advanced_things(self):
        self.objProcess.inject_dll("C:\\Dll1.dll")
        shellcode = b'\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xc3'
        self.objProcess.execute_shell_code(shellcode)

    # 进程所有模块信息
    def test_mod_info(self):
        vecMod = self.objProcess.get_mod_info()
        vecModInfoSize = self.objProcess.get_mod_info_size()
        print("szModule\t\t th32ProcessID\t\t th32ModuleID\t\t hModule\t\t \
            dwSize\t\t modBaseAddr\t\t modBaseSize\t\t \
            GlblcntUsage\t\t ProccntUsage\t\t szExePath\t\t")
        for i in range(0, vecModInfoSize):
            modPath = vecMod[i].modPath
            modName = vecMod[i].modName
            tagModEntry = vecMod[i].tagModEntry
            szModule = vecMod[i].tagModEntry.szModule
            th32ProcessID = vecMod[i].tagModEntry.th32ProcessID
            th32ModuleID = vecMod[i].tagModEntry.th32ModuleID
            hModule = vecMod[i].tagModEntry.hModule
            dwSize = vecMod[i].tagModEntry.dwSize
            modBaseAddr = vecMod[i].tagModEntry.modBaseAddr
            modBaseSize = vecMod[i].tagModEntry.modBaseSize
            GlblcntUsage = vecMod[i].tagModEntry.GlblcntUsage
            ProccntUsage = vecMod[i].tagModEntry.ProccntUsage
            szExePath = vecMod[i].tagModEntry.szExePath
            if szModule.lower() in "user32.dll":
                self.user32BaseAddr = modBaseAddr
            print("%s\t\t %x\t\t %x\t\t %x\t\t %x\t\t %x\t\t %x\t\t %x\t\t %x\t\t %s" 
            %(szModule, th32ProcessID, th32ModuleID, hModule, dwSize, modBaseAddr, modBaseSize, GlblcntUsage, ProccntUsage, szExePath))

    def format_mem_info(self, meminfo):
        nBaseAddress = meminfo.BaseAddress
        nAllocBase = meminfo.AllocationBase
        nAllocProtect = meminfo.AllocationProtect
        nRegionSize = meminfo.RegionSize
        nState = meminfo.State
        nProtect = meminfo.Protect
        nType = meminfo.Type

        # 初始化保护类型
        s_alloc_protect = ""
        if nAllocProtect & PAGE_NOACCESS:
            s_alloc_protect = "NoAccess"
        if nAllocProtect & PAGE_READONLY:
            s_alloc_protect = "Readonly"
        elif nAllocProtect & PAGE_READWRITE:
            s_alloc_protect = "ReadWrite"
        elif nAllocProtect & PAGE_WRITECOPY:
            s_alloc_protect = "WriteCopy"
        elif nAllocProtect & PAGE_EXECUTE:
            s_alloc_protect = "Execute"
        elif nAllocProtect & PAGE_EXECUTE_READ:
            s_alloc_protect = "Execute_Read"
        elif nAllocProtect & PAGE_EXECUTE_READWRITE:
            s_alloc_protect = "Execute_ReadWrite"
        elif nAllocProtect & PAGE_EXECUTE_WRITECOPY:
            s_alloc_protect = "Execute_WriteCopy"
        if nAllocProtect & PAGE_GUARD:
            s_alloc_protect = s_alloc_protect + "+Guard"
        if nAllocProtect & PAGE_NOCACHE:
            s_alloc_protect = s_alloc_protect + "+NoCache"  

        # 内存状态
        s_state = ""
        if nState == MEM_COMMIT:
            s_state = "Commit "
        elif nState == MEM_FREE:
            s_state = "Free   "
        elif nState == MEM_RESERVE:
            s_state = "Reserve"
        else:
            s_state = "Damned " 

        # 实际保护类型
        s_protect = ""
        if nProtect & PAGE_NOACCESS:
            s_protect = "NoAccess"
        if nProtect & PAGE_READONLY:
            s_protect = "Readonly"
        elif nProtect & PAGE_READWRITE:
            s_protect = "ReadWrite"
        elif nProtect & PAGE_WRITECOPY:
            s_protect = "WriteCopy"
        elif nProtect & PAGE_EXECUTE:
            s_protect = "Execute"
        elif nProtect & PAGE_EXECUTE_READ:
            s_protect = "Execute_Read"
        elif nProtect & PAGE_EXECUTE_READWRITE:
            s_protect = "Execute_ReadWrite"
        elif nProtect & PAGE_EXECUTE_WRITECOPY:
            s_protect = "Execute_WriteCopy"
        if nProtect & PAGE_GUARD:
            s_protect = s_protect + "+Guard"
        if nProtect & PAGE_NOCACHE:
            s_protect = s_protect + "+NoCache"  

        # 内存类型
        s_type = ""
        if nType == MEM_IMAGE:
            s_type = "Image  "
        elif nType == MEM_MAPPED:
            s_type = "Free   "
        elif nType == MEM_PRIVATE:
            s_type = "Private"
        else:
            s_type = "-      "  

        strMemInfo = "{}\t {}\t\t {}\t\t {}\t\t {}\t\t {}\t\t {}".format(str(hex(nBaseAddress)), str(hex(nAllocBase)), s_alloc_protect, s_state, s_protect, s_type, str(hex(nRegionSize)))
        return strMemInfo


    def print_mem_info(self, vecMem, vecMemInfoSize):
        print("基地址\t\t 申请地址\t\t 初始化保护类型\t\t 内存状态\t\t 实际保护类型\t\t 内存类型\t\t 内存大小\t\t 所属模块")
        for i in range(0, vecMemInfoSize):
            memBasicInfo = vecMem[i].memBasicInfo
            memInModPath = vecMem[i].memInModPath
            BaseAddress = vecMem[i].memBasicInfo.BaseAddress
            AllocationBase = vecMem[i].memBasicInfo.AllocationBase
            AllocationProtect = vecMem[i].memBasicInfo.AllocationProtect
            RegionSize = vecMem[i].memBasicInfo.RegionSize
            State = vecMem[i].memBasicInfo.State
            Protect = vecMem[i].memBasicInfo.Protect
            Type = vecMem[i].memBasicInfo.Type
            print(self.format_mem_info(vecMem[i].memBasicInfo))

    # 进程所有内存页面信息
    def test_mem_info(self):
        vecMem = self.objProcess.get_mem_info()
        vecMemInfoSize = self.objProcess.get_mem_info_size()
        self.print_mem_info(vecMem, vecMemInfoSize)


    # 进程内所有模块导出函数信息
    def test_expfun_info(self):
        vecExpInfo = self.objProcess.get_expfun_info()
        vecExpInfoSize = self.objProcess.get_expfun_info_size()
        print("函数地址\t\t 函数rva\t\t 函数名字\t\t 函数未粉碎c++签名")
        for i in range(0, vecExpInfoSize):
            funAddress = vecExpInfo[i].funAddress
            funRva = vecExpInfo[i].funRva
            funName = vecExpInfo[i].funName
            funSignature = vecExpInfo[i].funSignature
            if i > 100: # 导出函数可能很多 提前退出
                break
            print("%x %x %s %s" %(funAddress, funRva, funName, funSignature))

    def test_dis(self):
        #CODE = self.objProcess.get_binmem_by_region(0x77901000,0x1000)
        CODE = self.objProcess.get_binmem_by_module("user32.dll")
        md = Cs(CS_ARCH_X86, CS_MODE_32)
        for i in md.disasm(CODE, 0x1000):
            print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
        pass



def test_x32dbg():
    x32dbg = Test("Telegram.exe","Telegram.exe")
    x32dbg.test_mem_info()
    x32dbg.test_mod_info()
    x32dbg.test_expfun_info()
    x32dbg.test_get_mem()
    x32dbg.test_write_mem()
    x32dbg.test_dis()
    x32dbg.test_advanced_things()


def test_other():
    other = Test("MFCApplication2 - 副本.exe","ntdll.dll")
    other.test_get_mem()
    other.test_mem_info()
    other.test_mod_info()
    other.test_write_mem()


if __name__ == "__main__":
    test_x32dbg()
    test_other()
    pass

用法

  • 最简单的是将kar98k.pydtest.py放在同一目录 然后执行python test.py便可

最新版下载

https://github.com/stonedreamforest/kar98k_public/releases

更改日志

CHANGELOG


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-10-10 17:28 被Tennn编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (10)
雪    币: 29182
活跃值: (63621)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
感谢分享!
2019-10-9 16:33
0
雪    币: 7260
活跃值: (3869)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
asd
3
666 可否开源搞个py27版本
2019-10-9 17:39
0
雪    币: 9057
活跃值: (5301)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jgs
4
感谢分享,难得消化,收藏
2019-10-9 18:43
0
雪    币: 14653
活跃值: (17749)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
5
收藏从未停止,学习从未开始
2019-10-9 18:49
0
雪    币: 1176
活跃值: (1269)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
6
asd 666 可否开源搞个py27版本

暂不打算开源 如果有啥bug、问题或者功能建议 直接回帖或者发邮件或者打开
issue
image

 

python2.7 支持 我会看下难度

 

image

2019-10-9 19:07
0
雪    币: 977
活跃值: (435)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7
看帖从未停止,学习从未开始
2019-10-9 21:52
0
雪    币: 5649
活跃值: (3767)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8

函数签名及作用?

应该是函数声明吧

最后于 2019-10-10 12:00 被任蝶飞编辑 ,原因:
2019-10-10 11:59
0
雪    币: 1176
活跃值: (1269)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
9
任蝶飞 # 函数签名及作用? # 应该是函数声明吧

要表达意思基本一致 已修改
image

2019-10-10 12:43
0
雪    币: 1176
活跃值: (1269)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
10
asd 666 可否开源搞个py27版本
加了
2019-10-10 17:14
0
雪    币: 515
活跃值: (3322)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wem
11
好得很
2019-10-12 03:38
0
游客
登录 | 注册 方可回帖
返回
//