首页
社区
课程
招聘
[原创]调试器中不通过TF标记位实现单步操作的实现思路
发表于: 2010-8-21 17:27 14283

[原创]调试器中不通过TF标记位实现单步操作的实现思路

2010-8-21 17:27
14283
调试器中不通过TF标记位实现单步操作的实现思路
----besterChen       
               
        最近刚讲完Debug API,马上就要开始第三阶段写调试器的作业了。如果没有意外的话,这应该是我在科锐学习的最后一个编程类作业了吧,%>_<%。

        说实话,这个调试器写出来以后, 我用它的可能性不大。但作为作业,就索性往难处做,虽然可能不实用,甚至根本就没必要……, 就权当作是锻炼下自己吧。

       
我们都知道,如果程序不借助标志寄存器的TF位来实现单步步过和步入功能的话,我们只需要知道下一条指令的地址,然后在这个地址上随便下个int3或者硬件之类的断点,然后放开程序跑就可以实现单步步过或者步入了。至于难点应该就是怎么确定下一条要执行的代码的地址,由于需要考虑的情况比较多,故写此文档一来算是备忘,二来就算作抛砖引玉,看看各位师兄有没有更好的解决方法。
       
        进入主题。
一、类别统计
        一般程序都是顺序执行的,其下一条指令地址就是EIP+当前指令的长度作为下一条指令的位置,但是如果遇到转移类指令就需要做特殊的计算,这里列出已知的情况,供参考:
1、CALL 类指令
        CALL类指令,如果是步过的话,不需要特别计算,只要按照EIP+当前指令的长度的算法即可解决,下面讨论的是步入时的问题。
CALL  ADDR
        像这样的立即数寻址比较简单,可以通过取到机器指令中的相对地址部分,计算出目标地址;也可以通过反汇编引擎反汇编的代码来直接拿到目标地址。
CALL  [ADDR]
对于地址取内容的也比较简单,取到地址,自己ReadMemory就可以了。
CALL  [Reg]
        对于地址取内容的也比较简单,比较麻烦的就是取出相应寄存器的内容,但是有CONTEXT,也不麻烦的。取到内容以后自己ReadMemory也可以搞定。
CALL  [Reg+offset]
        这一类的指令多见于调用虚函数,也是CALL类指令中最复杂的,应为它需要我们自己计算出指针的值,通过指针ReadMemory才可以。
call    dword ptr [reg+reg*NUM+0xOFFSET]
这是我见过的最复杂的寻址方式….
RET
        对于RET指令,需要结合CONTEXT中ESP的内容来获取下一条指令的地址,这个应该是这些所有的情况中最简单的。
JCC类指令
        JCC类指令中JMP指令的情况与CALL相同,其它的条件指令先根据符号标记寄存器来判断跳转是否已经实现,如果跳转没有实现,其下一条指令地址就是EIP+当前指令的长度。下面讨论的是跳转已经实现的情况下。
JCC  ADDR
同CALL  ADDR的思路
JCC  [ADDR]
同 CALL [ADDR]的思路
JCC  [Reg] / JCC  [REG+OFFSET]
同CALL 的思路
JCC  [REG+REG*OFFSET]
        这是所有需要分析的指令中最复杂的情况,通过比例因子的寻址方式。我们不仅需要分离出各个元素及其内容,最难麻烦的是它的四则运算还需要考虑优先级。

二、整体的计算流程
        通过以上的统计,我们知道,有些情况下CALL类指令和JCC类指令的处理流程是一样的,所以,我整理的处理流程如下:

  • 检测当前指令是否是CALL或JCC类指令,如果不是,下一条指令就是 EIP + 当前指令长度
  • 检测指令是否为CALL类指令,如果指令是CALL类的指令就看下当前是步入还是不过,如果是步过,下条指令就是 EIP + 当前指令长度,如果是步入就继续步骤4。
  • 如果检测指令是JCC类指令,就如果是检测跳转是否实现,如果没实现下一条指令就是EIP + 当前指令长度;如果跳转实现,就继续步骤4。
  • 取出当前指令的操作数中中的所有的寄存器、立即数操作数以及操作符号。
  • 根据取出的内容,判断当前的转移指令是属于上面的哪个部分。
  • 对分离出的指令按照当前操作数中的规定进行运算得到目标地址


大致的说明性代码如下:
//************************************************************************
// 函数名: GetNextAddr
// 权  限: public 
// 返回值: DWORD 返回下一条指令的地址
// 参  数: BOOL isStepOver  TRUE为步过,FALSE为步入
// 说  明: 用于计算下一条指令所在地址的函数
// 合  格:
//************************************************************************
DWORD CDebugLib::GetNextAddr(BOOL isStepOver)
{
    DWORD    dwNextAddr = 0;
    TCHAR    szCommand[CODE_SIZE] = {0};    // 用来接收指令
    TCHAR    szCmdLine[CODE_SIZE] = {0};    // 用来接收反汇编出来的汇编指令

    t_disasm da                = {0};

    if (m_pBreakPoint == NULL)
    {
        return FALSE;
    }

	PCONTEXT tagpContext = GetContextValue();

	if (!tagpContext || tagpContext->Eip == NULL)
	{
		return FALSE;
	}

	int nLen = GetAsmCode((LPVOID)tagpContext->Eip, &da);
    _tcscpy(szCmdLine, da.result);
    _stscanf(szCmdLine, _T("%[^ ]"), szCommand); // 分离出指令来作判断
    _tcsupr(szCommand);

    if (_tcsicmp(szCommand, _T("CALL")) == 0)
    {
        if (isStepOver != TRUE)
        {
            // 对于单步步入的情况,需要计算步入的地址
            #pragma chMSG(CALL 指令的单步步入情况还没处理....)
            return dwNextAddr;
        }
        // 对于CALL的步过情况,与普通指令相同,这里就不处理了。
    }
    else if (_tcsstr(szCommand, _T("J")) != 0)
    {
        // 需要判断跳转实现没有
        if(IsJmpSuccess(tagpContext, szCommand))
        {
            // 如果跳转实现了,需要自己计算目标地址。
            _tprintf(_T("跳转已经实现....\r\n"));
            #pragma chMSG(JCC 指令中跳转实现的情况还没处理....)

            return dwNextAddr;
        }

        // 如果没有实现,与普通指令相同,这里就不处理了。
        _tprintf(_T("跳转没有实现....\r\n"));
    }
    else if (_tcsstr(szCommand, _T("RET")) != 0)
    {
        // 需要手工计算步入的地址
        assert(m_pBreakPoint->ReadMemory((LPVOID)tagpContext->Esp, \
               &dwNextAddr, sizeof(DWORD)));

        return dwNextAddr;
    }

    // 剩下的指令都是 EIP + 当前指令长度了。
    return (DWORD)((BYTE*)tagpContext->Eip+nLen);
}


三、实现的具体细节
以上列出的流程中,有好多的细节没有讲,来本小节中给我出的处理思路。
1、关于指令分离
        在我们判断当前指令是否为CALL或者JCC类指令时,就需要开始对当前指令进行词法分析了。当然,对指令的扫描不可能一步就进行完善,所以我们也分步骤的完成,先说对指令和操作数的分离:
       
        回顾一下X86下的汇编语法格式,一条指令中很少有3个操作数的情况,所以,我们暂且只考虑一个操作数和两个操作数的情况:
        汇编指令   操作数1 , 操作数2
        对于JCC或者CALL类转移指令中,都满足如下这样的格式:
        汇编指令   操作数
        是的,一般只有一个操作数,它遵循如下归于,指令与操作数之间一般有一个或多个空白字符(如空格或者TAB)分割,各个操作数之间用逗号分隔,由此,我们可以编写如下的代码来分离它们:
int nLen = GetAsmCode((LPVOID)tagpContext->Eip, &da); //得到当前指令的长度和反汇编的字符串
_stscanf(da.result, _T("%[^ ]"), tagCMDline.szCommand);		// 分离出指令部分
_stscanf(da.result, _T("%*[^ ] %[^,]"), tagCMDline.szParam1);	// 分离出指令的操作数1
_stscanf(da.result, _T("%*[^ ] %*[^,] , %[^,]"), tagCMDline.szParam2);	// 分离出操作数2


2、关于操作数的分离
        经过观察,我发现,一般的立即数寻址方式的操作数不需要分离,它直接就是我们想去的地址,我们只需要转换下数据类型,直接继续操作就可以了。
       
        而需要我们进行分离的操作数,有这样的规律:操作数以左方括弧为开始,以右方括弧结束,比如:
	[ebp+eax*4]

       
        对于这样的指令,用的是哪个寄存器,是什么样的格式,我们都很难猜到,也很难写出像上面分离指令那样的正则式。
       
        我们不仅要分离出操作数中的各个元素,还要对它们进行运算,对这种各个类型混合在一起的字串解析,我能想到的比较好的工具,就是状态机。
       
        分析这个格式的自动机如下:


根据此状态自动机,我们很容易的就能分离出操作数中的各种元素。接下来就是表达式运算了。
3、关于表达式的计算
        如果说,以上两小节的分离算作是词法分析的话,那本小节也就应该算是语法分析吧,╮(╯▽╰)╭
       
        一提起语法分析,不自觉的就想起什么BNF啊,语法树之类的名词,这个东西确实是让像我这样菜鸟头痛的东西,因为我几次手写的语法树都存在二义性,更别说用程序写……
       
                回顾一下我们这个操作数中的所有运算,它既没有括弧之类的token来改变优先级,也没有什么太复杂的运算(比如函数调用),甚至也最基本的四则运算中的除法运算都没有。因此,我感觉就实际情况而言,一个栈结构应该足够处理这里的所有情况了,并不用构建什么语法树,俗话说:杀鸡焉用宰牛刀?

                这里拿我们最复杂的操作数来说明解析过程,以 edx+eax*4+0x00401000为例来说明:

  • 取出 edx ,将其值存放到一个临时变量 A 中。
  • 取出+ 运算符,就想临时变量A的内容压栈。
  • 取出eax,将其值存放到一个临时变量A中。
  • 取出* 运算符,由于其优先级最高,就直接取下一个值。
  • 取出立即数 4, 将其与临时变量A的内容作乘法运算,并将结果再次存入临时变量A中。
  • 取出 + 运算符, 将临时变量A的值压栈。
  • 取出立即数 0x00401000,将其值存入临时变量A中。
  • 取值结束,将临时变量A的值压栈。
  • 依次求出栈中所有数据的和,得到最终的结果。


        整理一下,就是遇到 + 就压栈,遇到 * 就先求值再压栈,直到全部读取完毕。最后求出栈中所有数据之和。

        这里的词法分离和这简单的语法解析都比较容易,以上思路说明的已经比较透彻了。再者处理上面的步骤代码金量忒差,就不给出具体的实际代码了。
       
        当然,代码中,有一点还是值得说一下的,就是符号表的构建(我自己理解的不知道对不对….)。比如,我们在分析操作数时,得到了一个edx,我们的解释单元会直到edx属于关键字类型(具体分类见上面的自动机图),这样我们就需要一个符号表,将关键字这个类型中edx的值给返回出来,否则我们没法计算。
       
        大致的说明性代码如下:
	DWORD GetNodeValue(opera* tagpOper)
	{
	    switch( tagpOper->type )
	    {
	    case Reg:
	        /* 知道此时栈节点的类型为关键字,就需要根据EDX在符号表中找到EDX的值 */
	        return dwEdx;  
	    /* ... */
	    }
			return -1;
	}

        思路到这里就表述完成了。希望各位学长批评指正。(*^__^*) 嘻嘻……

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
免费 7
支持
分享
最新回复 (11)
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
2
难得今天上网,发现没有人讨论~
反而钱老师给了一个精华~
为了对得起这个精华,我就顺便贴下我的实现代码和效果图吧~(代码太挫……)
关于代码的分离部分:
//************************************************************************
// 函数名: Lex_Split
// 权  限: public 
// 返回值: list<TCHAR*>*        返回一个字符串链表
// 参  数: IN TCHAR * pszRegular
// 参  数: IN TCHAR * pszSource
// 说  明: 从指定的字符串中分离出各种子串
// 合  格:
//************************************************************************
list<TCHAR*>* Lex_Split(IN const TCHAR* pszRegular, IN const TCHAR* pszSource)
{
    int ntmpLen = 0;
    int nCount = 0; // 用来统计匹配的数量作为返回值
    match_results result; // 匹配结果
    list<TCHAR*> *pszResultList = new list<TCHAR*>;
    REGEX_FLAGS emRegEx*** = GLOBAL | ALLBACKREFS | NOCASE | SINGLELINE;
    rpattern pat((LPCTSTR)pszRegular, emRegEx***);  //正则模式及设置
    int iGroups = pat.cgroups();
    match_results::backref_type br = pat.match(pszSource, result );

    if (pszRegular == NULL || pszSource == NULL || br.matched == FALSE)
    {
        return NULL;
    }


    for( int i=0; i < result.cbackrefs(); i++ )
    {
        ntmpLen = _tcslen(result.backref(i).str().c_str());
        if( i%iGroups == 0 && ntmpLen > 0)
        {
            nCount++;
            
            TCHAR *pszResult = new TCHAR[ntmpLen+1];
            ZeroMemory(pszResult, ntmpLen+1);
            _tcscpy(pszResult, result.backref(i).str().c_str());
            pszResultList->push_back(pszResult);
        }
    }

    return pszResultList;
}


关于根据token获取值的部分:
//************************************************************************
// 函数名: GetRegTokenValue
// 权  限: public 
// 返回值: DWORD
// 参  数: IN CONTEXT * tagpContext
// 参  数: TCHAR * pszRegToken
// 说  明: 获取 分离出的各种token代表的值
// 合  格:
//************************************************************************
DWORD GetRegTokenValue(IN CONTEXT *tagpContext, TCHAR *pszRegToken)
{
    for (int i = 0; i < 8; i++)
    {
        if (_tcsicmp(st_RegInfo[i].pszString, pszRegToken) != 0)
        {
            continue;
        }

        switch(st_RegInfo[i].tagType.e)
        {
        case REG_TYPE::REG_TYPE_EAX:
             return tagpContext->Eax;

        case REG_TYPE::REG_TYPE_EBX:
             return tagpContext->Ebx;

        case REG_TYPE::REG_TYPE_ECX:
             return tagpContext->Ecx;
        case REG_TYPE::REG_TYPE_EDX:
             return tagpContext->Edx;
        case REG_TYPE::REG_TYPE_ESP:
             return tagpContext->Esp;
        case REG_TYPE::REG_TYPE_EBP:
             return tagpContext->Ebp;
        case REG_TYPE::REG_TYPE_ESI:
             return tagpContext->Esi;
        case REG_TYPE::REG_TYPE_EDI:
             return tagpContext->Edi;
        default:
            ;
        }
    }

    return -1;
}



//************************************************************************
// 函数名: GetRegTokenValue
// 权  限: public 
// 返回值: DWORD
// 参  数: TCHAR * pszCstToken
// 说  明: 获取 分离出的 常量 的值
// 合  格:
//************************************************************************
DWORD GetCstTokenValue(TCHAR *pszCstToken)
{
    DWORD dwResult = 0;
    if (pszCstToken == NULL)
    {
        return 0;
    }

    _stscanf(pszCstToken, _T("%x"), &dwResult);

    return dwResult;
}


整体上的模拟运算的流程如下:
//************************************************************************
// 函数名: CalcSimulator
// 权  限: public 
// 返回值: DWORD
// 参  数: IN CONTEXT * tagpContext
// 参  数: IN list<TCHAR * > * pTokenList
// 说  明: 表达式计算
// 合  格:
//************************************************************************
DWORD CalcSimulator(IN CONTEXT *tagpContext, IN list<TCHAR*>* pTokenList)
{
    int nCount = 0;
    DWORD dwResult = 0; // 前一个操作数
    DWORD dwCurNum = 0;
    if (tagpContext == NULL || pTokenList == NULL)
    {
        return -1;
    }

    if (pTokenList->size() <= 1)
    {
        return -1;
    }

    OPERATE_STATE em_OperReg;
    OPERATE_STATE em_OperCst;
    OPERATE_STATE em_OperSign;
    OPERATE_STATE em_OperStart;
    OPERATE_STATE em_OperEnd;

    em_OperReg.e = OPERATE_STATE::STATE_OPER_REG;
    em_OperCst.e = OPERATE_STATE::STATE_CONST_NUM;
    em_OperSign.e = OPERATE_STATE::STATE_SIGN_TOKEN;
    em_OperStart.e= OPERATE_STATE::STATE_SIGN_START;
    em_OperEnd.e = OPERATE_STATE::STATE_SIGN_END;

    pTokenList = CalcMulti(tagpContext, pTokenList); // 先处理乘法

    if (pTokenList == (list<TCHAR*>*)-1)
    {
        return -1;
    }

    nCount = pTokenList->size();

    list<TCHAR*>::iterator it = pTokenList->begin();
    for ( int i = 0 ; i < nCount ; i++ )
    {
        TCHAR *pResultBuf = NULL;
        pResultBuf = *it;

        if (pResultBuf == NULL)
        {
            continue;
        }
        
        if (Is(em_OperStart, pResultBuf))
        {
            // 清空栈内容
            while (!g_WaitForCalcStack.empty())
            {
                g_WaitForCalcStack.pop();
            }
        }

        if (Is(em_OperEnd, pResultBuf))
        {
            // 处理完成,开始计算栈中的数据
            int nSize = g_WaitForCalcStack.size();
            for (int i = 0; i < nSize; i ++)
            {
                dwResult += g_WaitForCalcStack.top();
                g_WaitForCalcStack.pop();
            }

            break;
        }


        if (Is(em_OperReg, pResultBuf))
        {
            // 如果是寄存器,就保存寄存器中的内容
            g_WaitForCalcStack.push(GetRegTokenValue(tagpContext, pResultBuf));
        }

        if (Is(em_OperCst, pResultBuf))
        {
            // 如果是立即数,就保存其值
            g_WaitForCalcStack.push(GetCstTokenValue(pResultBuf));
        }

        if (Is(em_OperSign, pResultBuf))
        {
            if (_tcsicmp(pResultBuf, _T("-")) == 0)
            {
                it++;
                //如果是减法,则需要将后面的数据取补保存
                pResultBuf = *it;

                if (Is(em_OperReg, pResultBuf))
                {
                    // 如果是寄存器,就保存寄存器中的内容
                    dwCurNum = GetRegTokenValue(tagpContext, pResultBuf);
                    g_WaitForCalcStack.push(dwCurNum);
                }

                if (Is(em_OperCst, pResultBuf))
                {
                    // 如果是立即数,就保存其值
                    dwCurNum = GetCstTokenValue(pResultBuf);
                    dwCurNum = (~dwCurNum)+1; // 求补码
                    g_WaitForCalcStack.push(dwCurNum);
                }
            }
        }

        it++;
    }

    return dwResult;

    由于乘法在我们当前的环境下优先级最高,所以我将乘法的计算过程分离出去了:
// 先计算乘法
static list<TCHAR*>* CalcMulti(IN CONTEXT *tagpContext, IN list<TCHAR*>* pTokenList)
{
    if (tagpContext == NULL || pTokenList == NULL)
    {
        return NULL;
    }
    TCHAR *pPreToken = NULL;
    TCHAR *pResultBuf = NULL;
    DWORD dwPreNum = 0; // 前一个操作数
    DWORD dwNextNum = 0; // 后一个操作数

    int   nCount = 0;
    OPERATE_STATE em_OperReg;
    OPERATE_STATE em_OperCst;
    OPERATE_STATE em_OperSign;
    OPERATE_STATE em_OperStart;
    OPERATE_STATE em_OperEnd;

    em_OperReg.e = OPERATE_STATE::STATE_OPER_REG;
    em_OperCst.e = OPERATE_STATE::STATE_CONST_NUM;
    em_OperSign.e = OPERATE_STATE::STATE_SIGN_TOKEN;
    em_OperStart.e= OPERATE_STATE::STATE_SIGN_START;
    em_OperEnd.e = OPERATE_STATE::STATE_SIGN_END;

    list<TCHAR*>* pResultList = new list<TCHAR*>;
    list<TCHAR*>::iterator it = pTokenList->begin();
    nCount = pTokenList->size();
    for ( int i = 0 ; i < nCount; i++ ) // 多循环了一次,需要看看
    {
        pResultBuf = *it;

        if (pResultBuf == NULL)
        {
            continue;
        }

        if (_tcsicmp(pResultBuf, _T("*")) != 0)
        {
            pResultList->push_back(pResultBuf);
        }

        if (i > 0)
        {
            it--;
            pPreToken = *it;
            it++;
        }

        if (pPreToken != NULL && _tcsicmp(pPreToken, _T("*")) == 0)
        {
            // 如果前一个是*也不保存当前的
            pResultList->pop_back();
        }


        if (Is(em_OperSign, pResultBuf) && _tcsicmp(pResultBuf, _T("*")) == 0)
        {
            pResultList->pop_back();
            // 先取出前一个操作数
            it--;
            pResultBuf = *it;
            if (Is(em_OperReg, pResultBuf))
            {
                // 如果是寄存器,就保存寄存器中的内容
                dwPreNum = GetRegTokenValue(tagpContext, pResultBuf);
            }

            if (Is(em_OperCst, pResultBuf))
            {
                // 如果是立即数,就保存其值
                dwPreNum = GetCstTokenValue(pResultBuf);
            }

            // 取出后一个操作数
            it++;
            it++;
            pResultBuf = *it;
            if (Is(em_OperReg, pResultBuf))
            {
                // 如果是寄存器,就保存寄存器中的内容
                dwNextNum = GetRegTokenValue(tagpContext, pResultBuf);
            }

            if (Is(em_OperCst, pResultBuf))
            {
                // 如果是立即数,就保存其值
                dwNextNum = GetCstTokenValue(pResultBuf);
            }
            
            TCHAR* pNewNum = new TCHAR[4];
            ZeroMemory(pNewNum, sizeof(TCHAR)*4);
            _stprintf(pNewNum, _T("%d"), dwPreNum*dwNextNum);
            pResultList->push_back(pNewNum);
            
            it--;
        }

        it++;
    }

    pTokenList->clear();
    if (pTokenList)
    {
        delete pTokenList;
        pTokenList = NULL;
    }

    pTokenList = pResultList;
    return pResultList;
}


主要的代码就这些了,也用不着我多说废话了,下面贴一下效果图:
先贴个简单的立即数寻址的:


CALL  reg形式的:


再看下 call [addr]这种形式的:


再看下return的效果:


最后来个超级复杂的那种(我自己修改代码构造的测试程序):


就到这里吧,希望各位师兄能多提点意见,也希望本文能对其它朋友有所帮助。
上传的附件:
2010-8-26 22:34
0
雪    币: 62
活跃值: (72)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
要活得下一条指令的地址。。。。。。。。

1.在本条指令处暂停,保存Context
2.单步一下
3.保持Context
4.活得eip
5.恢复Context
2010-8-27 10:59
0
雪    币: 45
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
LZ的意思好像是要实现你说的第二步,怎么单步一下……
2010-8-27 12:42
0
雪    币: 2239
活跃值: (483)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
5
楼主需要一个稍微好点的反汇编引擎.
或者自己解析opcode都比二次解析方便得多
2010-8-27 21:32
0
雪    币: 73
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
楼主强大...吾等小弟...膜拜
2010-8-28 18:17
0
雪    币: 334
活跃值: (22)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
很强大!!

不过有点绕弯了:把OPCODE反汇编成字符串,又把字符串分成了Token处理。
为什么不直接处理OPCODE呢?这样代码会少很多效率也会有所提高

像得到instuction [reg*4n + xxx]写一个GetModeRMValue函数即可了。

还有一点就是在用OD的时候当用F7单步sysenter的效果,这个思路是做不到的。

总体来说很强大了!100个支持
2010-8-29 23:31
0
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
8
[QUOTE=likunkun;852500]很强大!!

不过有点绕弯了:把OPCODE反汇编成字符串,又把字符串分成了Token处理。
为什么不直接处理OPCODE呢?这样代码会少很多效率也会有所提高

像得到instuction [reg*4n + xxx]写一个GetModeRMValue函数即可了。

还有一点就是在用OD的时候...[/QUOTE]

恩,不过,说实话,OpCode是啥我现在都没搞明白……

当时只是第一感觉是这样,我就这样做了。自己也感觉这样的做法不是很妥当,但是没有别的什么方法,所以发上来抛砖引玉,(*^__^*) 嘻嘻……

至于效率问题,我没考虑过,我觉得,单步的话,不用太考虑效率,毕竟人按键盘也不会太效率……

那个sysenter这个指令我还真没想到,我只想到了JCC, RETN, CALL……
2010-8-30 05:38
0
雪    币: 422
活跃值: (115)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
9
壮壮写的很强大啊。
你用的是啥反汇编引擎啊?
如果是OD的,他里面有指令类型,会告诉你当前指令是CALL,跳转之类的。还有寻址方式,好像有些也有说明。
2010-9-15 08:53
0
雪    币: 883
活跃值: (314)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
10
哎,前浪死在沙滩上,壮壮果然很强、很大!
2010-9-23 09:11
0
雪    币: 206
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
我们也要写调试器了,学习先
2010-11-4 23:20
0
雪    币: 45
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
mov [eax], 2;   eax = 0;
div eax;
....
....

且如果程序的异常处理中故意修改了eip..

这样的话是让异常进调试器的异常处理,然后在调试器中遍历异常链表?

如果异常链有100个handle,那就是先执行handle1,然后重新运行指令,如果该指令没修复,再执行handle2..?

郁闷中....
2010-11-25 15:36
0
游客
登录 | 注册 方可回帖
返回
//