- 注册时间
- 2011-1-27
- 最后登录
- 2021-8-19
- 在线时间
- 229 小时
终身VIP会员
花钱是让你服务的,不是叫你大哥 ...
- 魔鬼币
- 10632
|
分析下结构体,是为了向类过渡。
*针对C语言
问题原型:候选人得票的统计程序。设3个候选人, 每次输入一个得票的候选人的名字, 要求最后输出个候选人的得票结果。
C源码:
#include <stdio.h>
#include <string.h>
struct person
{
char name[20];
int count;
}leader[3] = {"Li",0, "Zhang",0, "Wang",0};
int main(void)
{
int i, j, n;
char leader_name[20];
printf("enter n:"); scanf("%d", &n);
for(i=0; i<n; i++)
{
scanf("%s", leader_name);
for(j=0; j<3; j++)
{
if(strcmp(leader_name, leader[j].name) == 0)
{
leader[j].count++;
break;
}
}
}
for(i=0; i<3; i++)
printf("%5s:%d\n", leader.name, leader.count);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////...
OD主程序反汇编代码(有点长,如果觉得没意思的话直接跳过看最后吧):
00401010 >|> \55 push ebp
00401011 |. 8BEC mov ebp,esp
00401013 |. 83EC 60 sub esp,0x60
00401016 |. 53 push ebx
00401017 |. 56 push esi
00401018 |. 57 push edi
00401019 |. 8D7D A0 lea edi,[local.24]
0040101C |. B9 18000000 mov ecx,0x18
00401021 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401026 |. F3:AB rep stos dword ptr es:[edi]
00401028 |. 68 30504200 push new18.00425030 ; /format = "enter n:"
0040102D |. E8 1E020000 call new18.printf ; \printf
00401032 |. 83C4 04 add esp,0x4
00401035 |. 8D45 F4 lea eax,[local.3]
00401038 |. 50 push eax
00401039 |. 68 2C504200 push new18.0042502C ; /format = "%d"
0040103E |. E8 AD010000 call new18.scanf ; \scanf
00401043 |. 83C4 08 add esp,0x8
00401046 |. C745 FC 00000>mov [local.1],0x0 //非常典型for语句的开端, i初始化为0
0040104D |. EB 09 jmp Xnew18.00401058
0040104F |> 8B4D FC /mov ecx,[local.1]
00401052 |. 83C1 01 |add ecx,0x1 //执行完for循环体后的表达式;
00401055 |. 894D FC |mov [local.1],ecx
00401058 |> 8B55 FC mov edx,[local.1]
0040105B |. 3B55 F4 |cmp edx,[local.3] //local.3 显然是局部变量n
0040105E |. 7D 67 |jge Xnew18.004010C7
00401060 |. 8D45 E0 |lea eax,[local.8] //获取输入字符串的指针
00401063 |. 50 |push eax //并压入
00401064 |. 68 28504200 |push new18.00425028 ; /format = "%s" 的首地址
00401069 |. E8 82010000 |call new18.scanf ; \scanf
0040106E |. 83C4 08 |add esp,0x8
// for(j=0; j<3; j++)
00401071 |. C745 F8 00000>|mov [local.2],0x0
00401078 |. EB 09 |jmp Xnew18.00401083
0040107A |> 8B4D F8 |/mov ecx,[local.2]
0040107D |. 83C1 01 ||add ecx,0x1
00401080 |. 894D F8 ||mov [local.2],ecx
00401083 |> 837D F8 03 | cmp [local.2],0x3
00401087 |. 7D 3C ||jge Xnew18.004010C5 //如果变量j超过3,则代表不存在这个候选人;
// 这里是这个程序的核心部分, 判断用户输入的名字是否与在结构体数组中存在
00401089 |. 8B55 F8 ||mov edx,[local.2] //edx存放变量j
0040108C |. 6BD2 18 ||imul edx,edx,0x18 // j = j * 18h (18h为结构体的大小)
0040108F |. 81C2 307A4200 ||add edx,offset new18.leader //结构体数组的首地址+j*18h 定位结构体数组的下标的首地址,这里就是结构体数组的成员变量name
00401095 |. 52 ||push edx //参数1 结构数组成员变量name字符串首地址
00401096 |. 8D45 E0 ||lea eax,[local.8] //local.8 就是当前的用户输入字符串的指针
00401099 |. 50 ||push eax //参数2 用户输入字符串首地址
0040109A |. E8 C1000000 ||call new18.strcmp //调用库函数strcmp,判断是否字符串完全相等
0040109F |. 83C4 08 ||add esp,0x8 //如果字符串完全相等,则返回eax=0
004010A2 |. 85C0 ||test eax,eax //如果eax=0, 则标志位寄存器zf 置 1
004010A4 |. 75 1D ||jnz Xnew18.004010C3 //zf = 0 则跳转
// 字符串相等的处理代码
004010A6 |. 8B4D F8 ||mov ecx,[local.2] //变量j
004010A9 |. 6BC9 18 ||imul ecx,ecx,0x18 //ecx = j + j*18h
004010AC |. 8B91 447A4200 ||mov edx,dword ptr ds:[ecx+0x427A44] //同(add edx,offset new18.leader) 同样是定位结构体数组下标指向的首地址
004010B2 |. 83C2 01 ||add edx,0x1 //count计数+1
//leader[j].count++;
004010B5 |. 8B45 F8 ||mov eax,[local.2]
004010B8 |. 6BC0 18 ||imul eax,eax,0x18
004010BB |. 8990 447A4200 ||mov dword ptr ds:[eax+0x427A44],edx
004010C1 |> EB 02 |\jmp Xnew18.004010C5 //第一个jmp,跳至004010c5
//字符串不相等则跳到这里
004010C3 |>^ EB B5 \jmp Xnew18.0040107A //第二个跳转,说明是字符串不相等才跳过来的,继续执行for(j=0; j<3; j++)循环;
004010C5 |>^ EB 88 jmp Xnew18.0040104F //是从第一个jmp跳过来的,继续跳回for(i=0; i<n; i++)处;
// 开始打印输出了。。。。以下略
004010C7 |? C745 FC 00000>mov [local.1],0x0
004010CE |> EB 09 /jmp Xnew18.004010D9
004010D0 |? 8B4D FC mov ecx,[local.1]
004010D3 |? 83C1 01 add ecx,0x1
004010D6 |? 894D FC mov [local.1],ecx
004010D9 |? 837D FC 03 cmp [local.1],0x3
004010DD |. 7D 29 |jge Xnew18.00401108
004010DF |? 8B55 FC mov edx,[local.1]
004010E2 |? 6BD2 18 imul edx,edx,0x18
004010E5 |? 8B82 447A4200 mov eax,dword ptr ds:[edx+0x427A44]
004010EB |? 50 push eax
004010EC |? 8B4D FC mov ecx,[local.1]
004010EF |? 6BC9 18 imul ecx,ecx,0x18
004010F2 |? 81C1 307A4200 add ecx,offset new18.leader ; ASCII "Li"
004010F8 |? 51 push ecx
004010F9 |? 68 1C504200 push new18.0042501C ; ASCII "%5s:%d
"
004010FE |? E8 4D010000 call new18.printf
00401103 |? 83C4 0C add esp,0xC
00401106 |>^ EB C8 jmp Xnew18.004010D0
00401108 |. 33C0 xor eax,eax
0040110A |. 5F pop edi
0040110B |. 5E pop esi
0040110C |? 5B pop ebx
0040110D |? 83C4 60 add esp,0x60
00401110 |. 3BEC cmp ebp,esp
00401112 |? E8 B9010000 call new18._chkesp
00401117 |. 8BE5 mov esp,ebp
00401119 5D pop ebp
注释:关于结构体的逆向,我觉得主要有两个问题:
第一:结构体数据在内存中如何存放?
第一个问题,和数组类似,结构体是线性的连续存放在内存中的。
第二:如何引用(操作)结构体(数组)成员变量?
这个问题也可以理解为, 如何定位结构体数组的成员变量。
让我们来看这几行代码:
00401089 |. 8B55 F8 ||mov edx,[local.2] //edx存放变量j
0040108C |. 6BD2 18 ||imul edx,edx,0x18 // j = j * 18h (18h为结构体的大小)
0040108F |. 81C2 307A4200 ||add edx,offset new18.leader //结构体数组的首地址+j*18h+0 定位结构体数组的下标的首地址,这里就是结构体数组的成员变量name
00401095 |. 52 ||push edx //参数1 结构数组成员变量name字符串首地址
我们定位一个结构体数组的成员变量, 首先需要知道该结构体的大小(占用的字节数) 。这里我们已经知道c源码了,所以不难发现结构体的大小为20+4=18h。
可是,当我们逆向分析一个无源码的程序时,只能通过逆向分析来抽丝剥茧的还原本来的代码。
这里很简单, imul edx,edx,0x18 这条指令告诉了我们该结构体的大小为18h
add edx,offset new18.leader 这条指令是为了定位 leader(i) 的首地址;这也是leader(i).name的首地址。当我们要指向成员变量count的时候,还需要add edx,14h。
004010BB |. 8990 447A4200 ||mov dword ptr ds:[eax+0x427A44],edx
这条指令一样, 0x427a44 和 offset new18.leader的地址是完全一致的。 |
评分
-
查看全部评分
|