📄 disasm.c
字号:
/* 将二进制文件中的机器指令翻译成32位intel格式汇编指令 */
/* 使用方法: disasm 文件名 ,如果输出多于一屏,可以使用重定向: disasm 文件名>res.txt 然后查看res.txt*/
/* 默认从偏移0开始翻译,也可以通过向DisAsmBinFile()传递参数指定偏移 */
/* 程序假设char型是1字节,long型至少是4字节.可以用vc编译,我想应该也可以用gcc,在linux下编译的话将四个#define中的\\替换成/就可以了.本来可以用win-tc编译,可是后来不知道加了什么功能后用win-tc编译出的程序运行有问题 */
/* DisAsmBinFile()函数负责反汇编,main()函数只负责打开待翻译文件和执行DisAsmBinFile(),因此容易扩展程序功能或将反汇编的程序代码移植到其它程序中 */
/* 我只写了80386指令,没有加入浮点指令和新出的指令,需要扩展的话在AnalyzeOpCode()中添加 */
/* version 1.0 ,2005.10.8.12:39 */
/* writer 又改名了 */
/* email ringken@gmail.com */
/* 源代码注释中的"操作码"均指除操作数以外的指令部份,而不是指整条指令,如mov eax,1的操作码是mov */
/* 源代码注释中的"指令"如单独作为一个词出现,均指整条指令的二进制代码 */
#include <stdio.h>
#include <string.h>
/********************************************************/
/* 定义数据文件路径 */
/********************************************************/
#define opcode_table "data\\opcode.dat"
#define opcode_table2 "data\\2Bopcode.dat"
#define opcode_grp_table "data\\extra.dat"
#define reg_table "data\\reg.dat"
/********************************************************/
/* 声明全局变量 */
/********************************************************/
char *writer = "ken";
char *email = "ringken@gmail.com";
FILE *BinFile; //指向要译码的bin文件
FILE *CodeTable; //指向操作码索引表
FILE *CodeTable2; //指向双字节操作码表
FILE *CodeGrpTable; //指向用于索引需要MODRM字段5,4,3位确定操作码的指令表
FILE *RegTable; //指向保存寄存器名称的索引表
struct
{
long current_code; //正在处理的指令在.bin文件中的偏移
int opcode_len; //正在处理的指令的操作码字节数,包括前缀码
int operand_len; //正在处理的指令的操作数字节数
unsigned char ins_buf[30]; //保存正在处理的指令代码
int processing; //分析指令时保存ins_buf中当前正在处理的字节
int prefix[4]; //指令前缀,ins.prefix[0]保存前缀66,[1]是前缀67,[2]是f0,f2,f3,[3]是6个段前缀
char code_data[16]; //从opcode表中取得的数据
char asm_code[50]; //临时存放反汇编后的字串
char *asm_code_writing; //指向asm_code[]中下一个应该写的位置
struct //MODRM
{
int mod;
int reg;
int rm;
} modrm;
struct //SIB
{
int s;
int index;
int base;
} sib;
long num; //在分析操作数时保存立即数或内存偏移
} ins;
/********************************************************************************/
/* 函数声明 */
/********************************************************************************/
void LoadDataFile(void); //载入数据文件
void CloseDataFile(void); //关闭数据文件
void InitInsStruct(void); //在处理新一条指令时初始化struct ins结构
void NumToText(int byte); //将ins.num中存放的byte个字节的数字转换成对应的16进制格式文本放到ins.asm_code[]中
//DisAsmBinFile()是执行反汇编的主函数
//由DisAsmBinFile()从要分析的BIN文件读出指令码放入ins.ins_buf中,调用分析操作码的函数AnalyzeOpCode()分析操作码并由AnalyzeOpCode()调用必要的函数完成操作码的分析
//如有操作数,由DisAsmBinFile()调用分析操作数的函数AnalyzeOperand()并由AnalyzeOperand()调用必要的函数完成操作数的分析
//如有操作数,DisAsmBinFile()从ins.code_data[]中取得待分析的操作数属性放入ins.code_data[8]开始的四个字节,由AnalyzeOperand()分析
//每分析完一条指令,DisAsmBinFile()根据操作码长度ins.opcode_len和操作数长度ins.operand_len计算下一条指令的偏移保存在ins.current_code中
//每分析完一条指令,指令的反汇编字串保存在ins.asm_code[]中,由DisAsmBinFile()输出
void DisAsmBinFile(long head); //从文件偏移的head处执行反汇编
//以下各函数分析操作码,计算指字中操作码的字节数保存在ins.opcode_len中,并处理操作码前缀保存在ins.prefix[]中
//分析完操作码后,ins.processing指向操作码后的第一个字节,分析操作数的函数会根据这个变量找到操作数
//AnalyzeOpCode()是分析操作码的主函数,由AnalyzeOpCode()调用必要的函数完成对操作码的分析
void AnalyzeOpCode(void); //根据opcode_table翻译操作码字段(包括前缀)
void ProcessPrefix(void); //翻译操作码前缀
void ModifyOpCode(void); //根据获得的前缀和操作码信息修改反汇编出的操作码部份,比如MOVSD改为MOVSW及确定REPE还是REP
void Process2ByteIns(void); //根据opcode_table2翻译2字节操作码
void Process3BitGroup(void); //根据opcode_grp_table翻译由MODRM的5,4,3位决定的操作码
//以下各函数分析操作数,计算指令中操作数的字节数保存在ins.operand_len中.除寻址方式B和I外都假设ins.ins_buf[ins.processing]为待处理的操作数
//寻址方式B可直接获得操作数,寻址方式I会根据ins.ins_buf[ins.processing]和已处理的操作数长度计算出ins.processing使其指向操作数,容易验证这样计算是正确的
//AnalyzeOperand()是分析操作数的主函数,由AnalyzeOpCode调用必要的函数完成对操作数的分析
//AnalyzeOperand()从ins.code_data[]中获得操作数寻址方式和操作数长度的信息
//AnalyzeOperImm()和AnalyzeOperModrm()函数还会根据正处理的操作数长度计算ins.operand_len,其它的寻址方式中B没有操作数,C、D、T、G、S的操作数是MODRM的reg字段,而只要有MODRM的reg字段,就必定有寻址方式E,M,R
void AnalyzeOperand(void); //根据code_data翻译操作数字段(操作数属性保存在ins.code_data[8]处2至4个字节),寻址方式B直接在此函数处理,其它寻址试要调用相应的函数处理
void AnalyzeOperReg(void); //根据code_data翻译操作数由MODRM的reg字段确定的情况,寻址方式为C,D,T,G,S
void AnalyzeOperImm(void); //根据code_data翻译操作数包含在指令中的情况,并计算本条操作数的长度.寻址方式为I,J
void AnalyzeOperModrm(void); //根据code_data翻译操作数由MODRM确定的情况以及寻址方式为O的情况,并计算本条操作数的长度.寻址方式为O,E,M,R
void GetModrm(void); //取得modrm字段
/********************************************************************************/
main(int argc, char *argv[])
{
if ( argc == 1)
{
puts("usage:\n\tdisasm filename");
exit(1);
}
if ( (BinFile = fopen(argv[1],"rb"))==NULL )
{
printf("Cannot open bin file %s",argv[1]);
exit(1);
}
DisAsmBinFile(0); //从文件偏移0处开始反汇编
}
void LoadDataFile(void)
{
if ( (CodeTable = fopen(opcode_table,"rb")) == NULL)
{
printf("Cannot open data file %s.",opcode_table);
exit(1);
}
if ( (CodeTable2 = fopen(opcode_table2,"rb")) == NULL)
{
printf("Cannot open data file %s.",opcode_table2);
exit(1);
}
if ( (CodeGrpTable = fopen(opcode_grp_table,"rb")) == NULL)
{
printf("Cannot open data file %s.",opcode_grp_table);
exit(1);
}
if ( (RegTable = fopen(reg_table,"rb")) == NULL)
{
printf("Cannot open data file %s.",reg_table);
exit(1);
}
return;
}
void CloseDataFile(void)
{
fclose(CodeTable);
fclose(CodeTable2);
fclose(CodeGrpTable);
fclose(RegTable);
return;
}
void NumToText(int byte) //将ins.num中存放的byte字节16进制数转成ASCII码存放在ins.asm_code[]中
{
byte = (byte<<3) -4; //把字节数转成要处理的位数,从 8*byte-4开始处理
for ( ; byte >= 0; byte -= 4)
{
*ins.asm_code_writing = ( (ins.num>>byte) & 0xf) +0x30; //16进制数转成ASCII码
if ( *ins.asm_code_writing > 0x39) //如果是A到F
*ins.asm_code_writing += 7;
ins.asm_code_writing ++;
}
return;
}
void DisAsmBinFile(long head) //执行反汇编的主函数
{
LoadDataFile();
ins.current_code = head; //设置反汇编的起点
while (1)
{
InitInsStruct(); //初始化ins
fseek(BinFile, ins.current_code, SEEK_SET); //将下一条指令读入缓冲区
if ( fread( (void*)(ins.ins_buf), 1, 10, BinFile) < 1 )
break;
AnalyzeOpCode(); //分析操作码部分并在ins.asm_code中填入操作码字串,包括前缀
if ( ins.code_data[8] ) //判断是否有操作数
{
ins.asm_code_writing = ins.asm_code+strlen(ins.asm_code); //移动指针
* ins.asm_code_writing++ = '\t';
AnalyzeOperand(); //分析第一操作数并在ins.asm_code中填入操作数字串
if ( (ins.code_data[8] != 'B') && ins.code_data[10]) //如果是三操作数指令
{
* ins.asm_code_writing++ = ',';
strncpy(ins.code_data+8,ins.code_data+10,2); //将操作数属性信息由ins.code_data+10处拷到ins.code_data+8处
AnalyzeOperand(); //分析第二操作数并在ins.asm_code中填入操作数字串
}
if ( ins.code_data[12] ) //如果还有操作数
{
* ins.asm_code_writing++ = ',';
strncpy(ins.code_data+8,ins.code_data+12,4); //将操作数属性信息由ins.code_data+12处拷到ins.code_data+8处
AnalyzeOperand(); //分析最后一个操作数并在ins.asm_code中填入操作数字串
}
}
//输出结果
printf("%08lX ",ins.current_code); //输出指令偏移
ins.current_code += (ins.opcode_len + ins.operand_len); //计算下一条指令的偏移
ins.processing = 0;
while ( ins.opcode_len-- ) //输出操作码部份的16进制数
printf("%02X",ins.ins_buf[ins.processing++]);
while ( ins.operand_len-- ) //输出操作数部份的16进制数
printf("%02X",ins.ins_buf[ins.processing++]);
ins.processing = 3 - ( ins.processing >>2); //计算需要输出的tab数,每个指令的操作数字串输出占3个tab,计算公式为tab数=3-(指令字节数*2)/8
while ( ins.processing-- )
putchar('\t');
printf("%s\n",ins.asm_code); //输出反汇编字串
}
CloseDataFile();
return;
}
void InitInsStruct(void) //初始化struct ins结构
{
ins.prefix[0] = 0;
ins.prefix[1] = 0;
ins.prefix[2] = 0;
ins.prefix[3] = 0;
ins.processing = 0;
ins.operand_len = 0;
ins.asm_code[0] = '\0';
return;
}
void AnalyzeOpCode(void) //根据code_data的前八个字节翻译操作码字段(包括前缀),code_data从数据文件中获得
{
fseek(CodeTable,ins.ins_buf[0]<<4, SEEK_SET); //从opcode表中读取ins.ins_buf缓冲区第一个字节的信息
fread( (void*)(ins.code_data), 16, 1, CodeTable) ;
while ( !ins.code_data[0] ) //若从opcode表中读出的不是一个操作码
{
switch (ins.code_data[1]) //第二个字节保存了编码的信息
{
case 1: ProcessPrefix(); //处理前缀码,并从opcode表中读出下一字节的信息放入ins.code_data
break;
case 2: Process2ByteIns(); //处理2字节指令码
break;
case 3: Process3BitGroup(); //处理由MODRM的5,4,3位决定的操作码
break;
case 4:
strcpy(ins.code_data,"387code!");
//ProcessX87Ins(); //处理协处理器指令集
break;
case 0: //不是一个机器指令码
ins.code_data[0] = ' ';
strcpy(ins.asm_code,"DB "); //输出16进制代码
ins.num = ins.ins_buf[0]; //将第一字节放入ins.num并设置ins.ins_code_writing交给NumToText()处理
ins.asm_code_writing = ins.asm_code+3;
NumToText(1);
* ins.asm_code_writing = '\0';
ins.processing = 0;
break;
}
}
if ( ins.prefix[1] || ins.prefix[2] ) //如果有前缀需要修改反汇编字串
ModifyOpCode(); //那就改吧
strcat( ins.asm_code,ins.code_data); //在ins.asm_code中保存反汇编得到的字串
ins.processing ++; //处理完操作码后ins.processing指向操作数字段
ins.opcode_len = ins.processing; //计算操作码长度
return;
}
void ProcessPrefix(void) //处理前缀码
{
switch (ins.ins_buf[ins.processing]) //看正在处理的指令字节是什么前缀
{
case 0x66: ins.prefix[0] = 0x66;
break;
case 0x67: ins.prefix[1] = 0x67;
break;
case 0xf0:
case 0xf2:
case 0xf3: ins.prefix[2] = ins.ins_buf[ins.processing];
break;
case 0x2e:
case 0x36:
case 0x3e:
case 0x26:
case 0x64:
case 0x65: ins.prefix[3] = ins.ins_buf[ins.processing];
break;
}
ins.processing ++;
fseek(CodeTable,ins.ins_buf[ins.processing]<<4, SEEK_SET); //从CodeTable中读入下一字节的信息
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -