首页
论坛
课程
招聘
[原创]用于将反汇编代码分段的Ida脚本
2007-3-30 10:05 5747

[原创]用于将反汇编代码分段的Ida脚本

2007-3-30 10:05
5747
当某个函数的逻辑关系比较复杂时,代码很不好看,所以写了这个脚本来辅助进行人工分析。
下一步会尝试写一个程序来分析这个脚本的输出文件,用来减轻人工分析的工作量。

代码主要作用:
   将某个模块的代码以跳转指令作为分界线,将代码分成若干代码段,
然后将代码段的信息输出到文件,并在给每个代码段的开始给代码段
赋予一个Part%d的名称,有利于对代码进行人工分析。

#include <idc.idc>

// Author : HenryShow.
// Version : V0.1
static main()
{
        // Declaration of vars
        auto CodeStart, CodeEnd, nIndex; // 分析的地址。
        auto PartInAddress, PartOutAddress1, PartOutAddress2; // 保存代码段的入口地址,以及出口地址
        auto nPartCount;        // 保存模块中代码段的个数
        auto Instruction, Operand1; // 保存分析出来的命令
        auto handle; // 文件句柄
        auto TmpString;        // 存放类似于Part%d的临时字串。
       
        // 打开文件,用来写入函数中代码段的信息。
        handle=fopen("Output.txt","wt");
        // 得到当前光标所在的地址作为开始地址
        CodeStart=ScreenEA();
        // 让用户手动输入结束地址
        CodeEnd = AskAddr(0,"Please insert ending address:");
        // 如果结束地址比开始地址还小,则结束脚本。
        if (CodeEnd <= CodeStart){
                fclose(handle);
                return;
        }
       
        // 代码段个数清零
        nPartCount = 0;
       
        nIndex = CodeStart;
       
        // 为代码段的入口地址设置初始值
        // 第一段的入口地址就是脚本分析的初地址
        PartInAddress = nIndex;
       
        PartOutAddress1 = nIndex;
        PartOutAddress2 = nIndex;
       
        // 开始循环判断
        while (nIndex < CodeEnd){
                if (nIndex == PartInAddress){
                        // 如果当前地址是某个代码段的入口地址,则在该地址添加Part%d的注释,以便于根据人工分析。
                        TmpString = "Part" + ltoa(nPartCount+1, 10);
                        MakeComm(nIndex, TmpString);
                }
                else{
                        // 否则将注释清除。
                        // 如果不需要清除,则将此行注释掉。
                        MakeComm(nIndex, "");
                }
               
                // 得到当前分析地址的指令字符串。
                Instruction = GetMnem(nIndex);
               
                if (substr(Instruction, 0, 1) == "j"){
                    // 如果指令是一个以j开头的指令,则为跳转指令。
                    // 那么指令的第一个操作数就是出口地址1。
                        fprintf(handle, "%d ", nPartCount);
                        fprintf(handle, "%.8X ", PartInAddress);
                        // 得到操作数对应的地址。
                        PartOutAddress1 = LocByName(GetOpnd(nIndex, 0));
                        if (PartOutAddress1 == -1){
                                // 取不到地址说明为$+*类型的操作数。
                                Operand1 = GetOpnd(nIndex, 0);
                                if (substr(Operand1, 0, 1) == "$"){
                                        if (substr(Operand1, 1, 2) == "+"){
                                                // $+*
                                                PartOutAddress1 = nIndex + atol(substr(Operand1, 2, -1));
                                        }
                                        else{
                                                //$-*
                                                PartOutAddress1 = nIndex - atol(substr(Operand1, 2, -1));
                                        }
                                }
                        }
                        fprintf(handle, "%.8X ", PartOutAddress1);
                       
                        if (substr(Instruction, 0, 3) != "jmp"){
                                // 如果不是jmp指令,下一条指令的地址就是出口地址2。
                                fprintf(handle, "%.8X", nIndex + ItemSize(nIndex));
                        }
                        else{
                                // 如果为jmp指令,则只有出口地址1有效。
                                fprintf(handle, "%.8X", 0);
                        }
                        fprintf(handle, "\n");
                        // 下一个代码段的入口地址就是下一条需要分析的指令的地址。
                        PartInAddress = nIndex + ItemSize(nIndex);
                        nPartCount++;
                }
                else if (Name(nIndex + ItemSize(nIndex)) != ""){
                        // 如果下一行有一个标号,那么从此地址开始创建下一个代码段。
                        // 并且将上一个代码段的出口地址1设置为当前地址,而出口地址2为无效地址。
                        fprintf(handle, "%d ", nPartCount);
                        fprintf(handle, "%.8X ", PartInAddress);
                        fprintf(handle, "%.8X ", nIndex + ItemSize(nIndex));
                        fprintf(handle, "%.8X", 0);
                        fprintf(handle, "\n");
                        PartInAddress = nIndex + ItemSize(nIndex);
                        nPartCount++;
                }
                       
                // 继续分析下一条指令。
                nIndex = nIndex + ItemSize(nIndex);
        }
       
        fprintf(handle, "%d ", nPartCount);
        fprintf(handle, "%.8X ", PartInAddress);
        fprintf(handle, "%.8X ", 0);
        fprintf(handle, "%.8X", 0);
        fprintf(handle, "\n");
       

        // 关闭文件句柄。
        fclose(handle);
        return;
}

【看雪培训】《Adroid高级研修班》2022年春季班招生中!

收藏
点赞0
打赏
分享
最新回复 (4)
雪    币: 521
活跃值: 活跃值 (151)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2007-3-30 12:00
2
0
直接用IDA,不可以看吗?

简单乱序过的,这样重排,倒是不错的。

可是没必要输出到文件中嘛。。。反而不方便了。。。

注释挺多的,对新人学习IDC不错。
雪    币: 199
活跃值: 活跃值 (160)
能力值: ( LV12,RANK:2670 )
在线值:
发帖
回帖
粉丝
KuNgBiM 活跃值 66 2007-3-30 13:37
3
0
雪    币: 202
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
HenryShow 活跃值 1 2007-3-30 15:15
4
0
输出到文件中是为了给自己的程序使用,想通过自己写的程序来翻译成
类似于C的代码,但思路还没有完全理清。

===============================================================
我的想法:
    每个生成的代码段,都有3个点,1个入口,2个出口。

    对于顺序结构的代码段,只有1个入口,1个出口,
    即      InAddr   ->   OutAddr1     (OutAddr2为无效)

    对于判断结构,则有1个入口,2个出口。
    即               
                 InAddr
           +--------+----------+
        OutAddr1             OutAddr2

    循环结构则是特殊的判断结构,其中OutAddr1 = InAddr,或者OutAddr2 = InAddr。 如果OutAddr1 = OutAddr2 = InAddr,则为死循环。

    如果可以用程序将代码段分析成上述的结构,应该可以把程序转换成相应的C语言的流程。

    程序还在编写当中,如果有消息我会第一时间发布上来给大家测试。
雪    币: 521
活跃值: 活跃值 (151)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2007-3-31 14:18
5
0
工具专区以前就有人发过反编译成C STYLE代码的工具。。我下了没用过。。据说是挺好用的

开始的时候,建议先不要想太复杂的情况

对代码流程处理,我看过一些相关理论,什么CFG图的,是假设只有一个入口和一个出口的。

我对这方面不懂,或者你可以去翻译区看看,对抗病毒方面的文献几乎都有提到这个问题。
游客
登录 | 注册 方可回帖
返回