首页
社区
课程
招聘
LastError v0.0.1 (Ida 小插件)
2005-10-17 09:11 10086

LastError v0.0.1 (Ida 小插件)

2005-10-17 09:11
10086
LastError v0.0.1 (Ida 小插件)

作者: 一块三毛钱
邮箱: zhongts@163.com
日期: 2005.10.17

使用 VC 调试可以在 watch 窗口添加 @err,hr 查看 API 调用的错误,OllyDBG 也可以查看 API 调用错误。由于 Ida 动态调试的时候没有这项功能(可能是我没有找到^_^),所以我编写了这个个小插件。



因为是第一次学习 Ida 插件的编写,很多函数的使用和调试方法都不知道,所以采用了代码中给出的拙劣手段。通过逆向 Ida 的插件模块 win32_user.plw 找到处理调试事件的代码处,把处理 EXCEPTION_DEBUG_EVENT 事件的过程地址替换成我的函数的地址,处理完后再返回原来的代码。本来是想注册通知消息 hook_to_notification_point() 来调用我的代码,但是找来找去没有找到应该使用哪一个消息,dbg_exception、dbg_breakpoint 等好象都不行。在跳转到我的代码中,首先通过 GetThreadContext 函数取得被调试进程的 fs 寄存器的值,然后读取 TEB 结构中的 LastErrorValue 成员的值得到 API 函数调用的错误。因为需要转换选择子,所以先通过 GetThreadSelectorEntry 函数把 fs 选择子转换成线性地址,然后再读取。代码中很多地方都采用了迂回的办法,本来是想通过函数 get_reg_val 读取 fs 寄存器的值,结果不管读取哪个寄存器的值该函数老是失败,不知道为什么。有没有哪位大侠知道?

/*
* LastError v0.0.1 by 一块三毛钱 (2005.10.16)
*
* http://zhongts.reg365.com   zhongts@163.com
*
* IDA 调试插件,调试过程中获取 API 函数调用失败的原因
*/

#include <windows.h>

#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <dbg.hpp>

#pragma comment(linker,"/OPT:NOWIN98")

char IDAP_comment[] = "LastError v0.0.1 by zhongts (" __DATE__ " " __TIME__ ")";
char IDAP_help[] = "GetLastError() utility\n";

char IDAP_name[] = "LastError";
char IDAP_hotkey[] = "Alt-X";

int         *lpTableAddr;
int         lpAddr;

DWORD       pid, tid;

CONTEXT     MyContext;
LDT_ENTRY   SelEntry;

HANDLE      hProcess, hThread;
DWORD       dwSegAddr;
DWORD       dwReturn;
int         precode, code;

static void __declspec(naked) LastError(void)
{
    /* 进入该函数的时候,[ebp-150h] 表示 DEBUG_EVENT 结构 (ida480) */
    __asm
    {
        pushad
        mov     eax, [ebp-14Ch]
        mov     [pid], eax
        mov     eax, [ebp-148h]
        mov     [tid], eax
    }

//  msg(">>> PID = %d, TID = %d\n", pid, tid);

    /* 读取线程中的 fs 寄存器的值 */
    hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, 0, tid);
    if (hThread == NULL)
        goto exit_0;

    MyContext.ContextFlags = CONTEXT_SEGMENTS;
    GetThreadContext(hThread, &MyContext);

    /* 为了读取 fs:[18h] 的值,需要转换选择子 fs 为线性地址 */
    GetThreadSelectorEntry(hThread, MyContext.SegFs, &SelEntry);

    dwSegAddr = ( SelEntry.HighWord.Bits.BaseHi << 24) |
                (SelEntry.HighWord.Bits.BaseMid << 16) |
                SelEntry.BaseLow;

//  msg(">>> fs = 0x%X, FS = 0x%X\n", MyContext.SegFs, dwSegAddr);

    CloseHandle(hThread);

    hProcess = OpenProcess(PROCESS_VM_READ, 0, pid);
    if (hProcess == NULL)
        goto exit_0;

    /* 读取 fs:[18h] 处的值得到 TEB 结构的地址 */
    dwSegAddr += 0x18;
    if (ReadProcessMemory(hProcess, (LPCVOID)dwSegAddr, &dwSegAddr, 4, &dwReturn))
    {
        /* 读取 [TEB].LastErrorValue 的值得到 LastError */
        dwSegAddr += 0x34;
        if (ReadProcessMemory(hProcess, (LPCVOID)dwSegAddr, &code, 4, &dwReturn))
        {
            if (code != precode)
            {
                msg(">>> GetLastError() = %X (%s)\n", code, winerr(code));
                precode = code;
            }
        }
    }

    CloseHandle(hProcess);

    goto exit_1;

exit_0:
    msg(">>> failed! (%s)\n", winerr(GetLastError()));
exit_1:
    __asm   popad
    __asm   jmp [lpAddr]
}

int IDAP_init(void)
{
    if ( ph.id != PLFM_386 || inf.filetype != f_PE )
        return PLUGIN_SKIP;

    msg(">>> %s\n", IDAP_comment);
   
    return PLUGIN_KEEP;
}

void IDAP_term(void)
{
    /* 还原被修改的跳转表中的地址 */
    if (lpTableAddr && lpAddr)
        *lpTableAddr = lpAddr;

    return;
}

void IDAP_run(int arg)
{
    /* 找到调试模块中处理 EXCEPTION_DEBUG_EVENT 信息的地方,把地址替换成我的函数 */
    lpTableAddr = (int *)GetModuleHandle("win32_user.plw");
    if (lpTableAddr == NULL)
    {
        msg(">>> no found debug module win32_user.plw\n");
        return;
    }

    lpTableAddr = (int *)((char *)lpTableAddr + 0xB860);    // ida480

    if (*lpTableAddr != (int)LastError)
    {
        lpAddr = *lpTableAddr;
        *lpTableAddr = (int)LastError;
    }

    msg(">>> Patch: Old = %p, New = %p\n", lpAddr, LastError);

    return;
}

plugin_t PLUGIN =
{
    IDP_INTERFACE_VERSION,
    0,
    IDAP_init,
    IDAP_term,
    IDAP_run,
    IDAP_comment,
    IDAP_help,
    IDAP_name,
    IDAP_hotkey
};

因为用到了硬编码,该插件应该是只能在 ida480 上面使用,如果是用别的版本的请自己修改代码。

参考资料:

[1] Ida Plug-In Writing In C/C++,Steve Micallef
    http://www.binarypool.com/idapluginwriting/

附件:lasterror.rar

[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞7
打赏
分享
最新回复 (14)
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
shoooo 16 2005-10-17 09:25
2
0
强贴必留名, 我顶
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
prince 16 2005-10-17 10:52
3
0
牛人,终于用C出手啦!
雪    币: 323
活跃值: (343)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
doskey 4 2005-10-17 11:39
4
0
最近经常看见老兄的文章呀。到处都有……
雪    币: 389
活跃值: (912)
能力值: ( LV9,RANK:770 )
在线值:
发帖
回帖
粉丝
kyc 19 2005-10-17 13:02
5
0
强贴留名
雪    币: 234
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
lnn1123 13 2005-10-17 13:30
6
0
强贴留名.
雪    币: 153
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Phoenix 2005-10-17 13:58
7
0
强帖必顶
雪    币: 200
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dingshan 2005-10-17 17:23
8
0
留名先
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xingbar168 2005-10-20 19:30
9
0
这个是干什么的 ,说明白点
雪    币: 367
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DarkNess0ut 2005-11-11 13:32
10
0
ea_t TEB;
regval_t val;
get_reg_val("FS",&val);
dbg->thread_get_sreg_base(dbget.tid,val.ival,&TEB);

fs:0可以这么读取
雪    币: 337
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
nbw 24 2005-11-11 21:57
11
0
留名!

高人阿
雪    币: 277
活跃值: (321)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
一块三毛钱 11 2005-11-14 16:25
12
0
最初由 DarkNess0ut 发布
ea_t TEB;
regval_t val;
get_reg_val("FS",&val);
dbg->thread_get_sreg_base(dbget.tid,val.ival,&TEB);

........

本来也是想这样编码,但是 get_reg_val 老是失败,不知道什么原因。
雪    币: 367
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DarkNess0ut 2005-11-14 16:39
13
0
VC有提示
static void __declspec(naked)  这样的函数里不能用get_reg_val()
所以要用的话还是先获得FS值再进入lasterror获取fs:0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
云风 2005-11-14 19:57
14
0
需要好好研究一下。
雪    币: 437
活跃值: (228)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
bzhkl 5 2011-2-25 23:56
15
0
本来是想通过函数 get_reg_val 读取 fs 寄存器的值,结果不管读取哪个寄存器的值该函数老是失败,不知道为什么。有没有哪位大侠知道?

// The debugger structure defines a set of hardware registers in dbg->registers.
// IDA also recognizes register names for each defined bit in bit registers.
// You can use all these names to set or get a register value.
//
// For example, with the x86 Userland Win32 debugger you can use
// register names like:
//  - "EAX", ... "EBP", "ESP", "EFL": for classical integer registers
//  - "CS", "DS", ...               : for segment registers
//  - "ST0", "ST1", ...             : for FPU registers
//  - "CF", "PF", "AF", "ZF", ...   : for special bit values

// Read a register value from the current thread.
// Type:         Synchronous function
// Notification: none (synchronous function)

Read a register value from the current thread.
应该是你调用的时候,IDA不知道你要访问哪个线程的寄存器,因为你的程序是异步的,所以get_reg_val一般在注册回调函数中调用。

如果想调用get_reg_val, 可以使用同步调试模式。
游客
登录 | 注册 方可回帖
返回