滴水逆向PE前复习漏洞
1. 只用逻辑运算2-3=?
2-3=-1 #用计算机的逻辑运算本质运算
X:0010 #将2存入容器X
Y:1101 #将-3存入容器Y
0010
xor 1101 #异或
1111 #值为-1
0010
& 1101
0000 #与运算后得出0
0000 <<1 == 0000 #左移一位还是0000,得出结果就是1111即 -1
2. 标志寄存器的位置
-
进 位 标 志 C F ( C a r r y F l a g ) \color{blue}{进位标志CF(Carry Flag)} 进位标志CF(CarryFlag):如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0;
-
辅 助 进 位 标 志 A F ( A u x i l i a r y C a r r y F l a g ) \color{blue}{辅助进位标志AF(Auxiliary Carry Flag)} 辅助进位标志AF(AuxiliaryCarryFlag):
在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
(1)、在字操作时,发生低字节向高字节进位或借位时;
(2)、在字节操作时,发生低4位向高4位进位或借位时
-
溢 出 标 志 O F ( O v e r f l o w F l a g ) \color{blue}{溢出标志OF(Overflow Flag)} 溢出标志OF(OverflowFlag):溢出标志OF用于反映有符号数加减运算所得结果是否溢出。
如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。
注:
最高位进位与溢出的区别:
>进位标志表示无符号数运算结果是否超出范围.
>溢出标志表示有符号数运算结果是否超出范围.
>OF位溢出主要是给有符号运算使用的,在有符号的运算中,有如下的规律:
>正 + 正 = 正 如果结果是负数,则说明有溢出
>负 + 负 = 负 如果结果是正数,则说明有溢出
>正 + 负 永远都不会有溢出.
3. CPU是如何计算2+3=?
2+3 = ?
X:0010
Y:0011 R:0001
0010 0001
xor 0011 xor 0100 R:0101
------------- ---------------
0001 0101 5
0010 0001
& 0011 & 0100
------------ ------------
0010 0000
0010 << 1 == 0100 0000<<1 == 0000
X:0001
Y:0100
4. 获取某个值的第N位的值是多少?
如:8F
如:8F
10001111
and 00001000
-----------
00001000
5. 常见的几种调用约定
6. console程序入口查找
-
程序真正的入口
main 或 Winmain 是“语法规定的用户入口”,而不是“应用程序的入口”,应用程序的入口通常是start函数
7. 变量的特点
8. 参数传递的问题
-
参数传递不仅仅是 push 之后
1.观察调用函数
2.找平衡堆栈的代码(内部 ret 或外部 add esp 0x~) -
其它方式
9. if…else…反汇编练习
练习一
-
函数内部变量分析
1. 分析参数: [ebp + 8]:x [ebp + 0xC]:y 2. 分析局部变量 [ebp - 4]:a [ebp - 8]:b = 2; 3. 分析全局变量 [004225c4]:k
-
函数功能分析
-
还原c语言代码
#include<stdio.h> int k; void plus(int x,int y) { int a = k; int b = 2; if(x >= y) { b = b +1; if(x < y) { k = b; } } else { k = a+ b; } } int main(int argc,char* argv[]) { plus(3,6); //getchar(); return 0; }
练习二
10. 类型转换
- 发现一个 c 代码没写过,有点新奇
int age[5][10] = { {1,2,3,4,5,6,7,8,9,10}, {21,22,23,24,25,26,27,28,29,30}, {31,32,33,34,35,36,37,38,39,40}, {41,42,43,44,45,46,47,48,49,50}, {51,52,53,54,55,56,57,58,59,60} }; void fun(){ for (int i=0;i<5;i++) { for (int j=0;j<9;j++) //这里是前面一个数组加后面一个,到了倒数第二个的时候刚好把最后一个加上去了,所以是j<9 { age[i][0] = age[i][0] + age[i][j+1]; //这里j+1意思就是前面一个数组加后面一个数组 } } for (int k=0;k<5;k++) { printf("%d \n",age[k][0]); } }
11. 循环
12. 64位存储方式
- 返回值超过32位时,存在哪里?用long long(__int64)类型做实验
long long类型在VC6中对应的是__int64 __int64 Function() { __int64 x = 0x1234567890; return x; }
>>>>64位存储方式是连续存储高字节的存在ebp-8里面,低字节的存在ebp-c里面,返回值是通过eax,dex寄存器返回的
13. 结构体作为返回值
struct st1
{
int a;
int b;
};
struct st
{
char a4;
short b4;
int c4;
int arr4[10];
st1 d4;
};
st Function()
{
st s;
st.a4 = 6; // mov byte ptr [ebp-50h],6
st.arr4[2] = 76; // mov dword ptr [ebp-40h],4Ch
st.b4 = 5; // mov word ptr [ebp-4Eh],offset fun+60h (0040b9e0)
st.c4 = 88; // mov dword ptr [ebp-4Ch],58h
st.d4.a = 90; // mov dword ptr [ebp-20h],5Ah
return s; // mov ecx,0Eh ;lea esi,[ebp-50h] ;mov edi,dword ptr [ebp+8] ;rep movs dword ptr [edi],dword ptr [esi] ;mov eax,dword ptr [ebp+8]
};
int main(int argc, char* argv[])
{
st s = Function();
return 0;
}
14. movs 与 stos 指令
-
M O V S \color{blue}{MOVS} MOVS指令:移动数据 内存-内存
BYTE/WORD/DWORD
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] ~~~~~简写为:MOVSB
MOVS WORD PTR ES:[EDI],BYTE PTR DS:[ESI] ~~~~~简写为:MOVSW
MOVS DWORD PTR ES:[EDI],BYTE PTR DS:[ESI] ~~~~~简写为:MOVSD -
S T O S \color{blue}{STOS} STOS指令:讲Al/AX/EAX的值存储到[EDI]指定的内存单元
STOS BYTE PTR ES:[EDI] ~~~~~简写为STOSB
STOS WORD PTR ES:[EDI] ~~~~~简写为STOSW
STOS DWORD PTR ES:[EDI] ~~~~~简写为STOSD
详见
15. switch反汇编
-
case 小于等于3时同 if…else…
-
case 大于3时会生成对应的大表
1: void fun(int x) 2: { 0040B800 push ebp 0040B801 mov ebp,esp 0040B803 sub esp,44h 0040B806 push ebx 0040B807 push esi 0040B808 push edi 0040B809 lea edi,[ebp-44h] 0040B80C mov ecx,11h 0040B811 mov eax,0CCCCCCCCh 0040B816 rep stos dword ptr [edi] 3: switch (x) 4: { 0040B818 mov eax,dword ptr [ebp+8] 0040B81B mov dword ptr [ebp-4],eax 0040B81E mov ecx,dword ptr [ebp-4] 0040B821 sub ecx,64h 0040B824 mov dword ptr [ebp-4],ecx 0040B827 cmp dword ptr [ebp-4],9 0040B82B ja $L815+0Fh (0040b8d7) 0040B831 mov edx,dword ptr [ebp-4] 0040B834 jmp dword ptr [edx*4+40B8F5h] 5: case 100: 6: printf("100\n"); 0040B83B push offset string "100\n" (00420fc8) 0040B840 call printf (00401110) 0040B845 add esp,4 7: break; 0040B848 jmp $L815+1Ch (0040b8e4) 8: case 101: 9: printf("101\n"); 0040B84D push offset string "101\n" (00420fc0) 0040B852 call printf (00401110) 0040B857 add esp,4 10: break; 0040B85A jmp $L815+1Ch (0040b8e4) 11: case 102: 12: printf("102\n"); 0040B85F push offset string "102\n" (00420fb8) 0040B864 call printf (00401110) 0040B869 add esp,4 13: break; 0040B86C jmp $L815+1Ch (0040b8e4) 14: case 103: 15: printf("103\n"); 0040B86E push offset string "103\n" (00420f84) 0040B873 call printf (00401110) 0040B878 add esp,4 16: break; 0040B87B jmp $L815+1Ch (0040b8e4) 17: case 104: 18: printf("104\n"); 0040B87D push offset string "104\n" (00420fb0) 0040B882 call printf (00401110) 0040B887 add esp,4 19: break; 0040B88A jmp $L815+1Ch (0040b8e4) 20: case 105: 21: printf("105\n"); 0040B88C push offset string "105\n" (00420fa8) 0040B891 call printf (00401110) 0040B896 add esp,4 22: break; 0040B899 jmp $L815+1Ch (0040b8e4) 23: case 106: 24: printf("106\n"); 0040B89B push offset string "106\n" (00420fa0) 0040B8A0 call printf (00401110) 0040B8A5 add esp,4 25: break; 0040B8A8 jmp $L815+1Ch (0040b8e4) 26: case 107: 27: printf("107\n"); 0040B8AA push offset string "107\n" (00420f98) 0040B8AF call printf (00401110) 0040B8B4 add esp,4 28: break; 0040B8B7 jmp $L815+1Ch (0040b8e4) 29: case 108: 30: printf("108\n"); 0040B8B9 push offset string "108\n" (00420024) 0040B8BE call printf (00401110) 0040B8C3 add esp,4 31: break; 0040B8C6 jmp $L815+1Ch (0040b8e4) 32: case 109: 33: printf("109\n"); 0040B8C8 push offset string "109\n" (00420f90) 0040B8CD call printf (00401110) 0040B8D2 add esp,4 34: break; 0040B8D5 jmp $L815+1Ch (0040b8e4) 35: default: 36: printf("Error\n"); 0040B8D7 push offset string "Error\n" (0042001c) 0040B8DC call printf (00401110) 0040B8E1 add esp,4 37: break; 38: } 39: } 0040B8E4 pop edi 0040B8E5 pop esi 0040B8E6 pop ebx 0040B8E7 add esp,44h 0040B8EA cmp ebp,esp 0040B8EC call __chkesp (00401190) 0040B8F1 mov esp,ebp 0040B8F3 pop ebp 0040B8F4 ret 0040B8F5 cmp edi,dword ptr [eax-47B2FFC0h] 0040B8FB inc eax 0040B8FC add byte ptr [edi-48h],bl 0040B8FF inc eax 0040B900 add byte ptr [esi-48h],ch 0040B903 inc eax 0040B904 add byte ptr [ebp-48h],bh 0040B907 inc eax 0040B908 add byte ptr [eax+edi*4-4764FFC0h],cl 0040B90F inc eax 0040B910 add byte ptr [edx-46FFBF48h],ch 0040B916 mov eax,0B8C80040h 0040B91B inc eax 0040B91C add ah,cl
-
根据大表计算
64 - - 十进制 100
106-100 = 6
edx*4+40B8F5h
从上面计算公式得出
6×4+40B8F5 意思就是0040B8F5所在的大表向下以4个字节移动6位 -
大表
0040B8F5 3B B8 40 00
0040B8F9 4D B8 40 00
0040B8FD 5F B8 40 00
0040B901 6E B8 40 00
0040B905 7D B8 40 00
0040B909 8C B8 40 00
0040B90D 9B B8 40 00
0040B911 AA B8 40 00
0040B915 B9 B8 40 00
0040B919 C8 B8 40 00 -
向下以4字节移动6位刚好就是大表地址 0040B90D 9B B8 40 00 对应的内存地址: 0040B89B
23: printf(“106\n”);
0040B89B push offset string “106\n” (00420fa0)
0040B8A0 call printf (00401110)
0040B8A5 add esp,4 -
此时去掉 c a s e 108 \color{orange}{case 108} case108,则生成的大表为
0040B8F5 3B B8 40 00
0040B8F9 4D B8 40 00
0040B8FD 5F B8 40 00
0040B901 6E B8 40 00
0040B905 7D B8 40 00
0040B909 8C B8 40 00
0040B90D 9B B8 40 00
0040B911 AA B8 40 00
0040B915 B9 B8 40 00 将变为 D7 B8 40 00
0040B919 C8 B8 40 00 -
结论
- 当抹去case语句小于等于5个时,会将default的内存地址编号放入大表中;
- 当抹去6个case语句的时候会生成小表(case较多但连续性不是太好)
7: void fun(int x) 8: { 0040B800 push ebp 0040B801 mov ebp,esp 0040B803 sub esp,44h 0040B806 push ebx 0040B807 push esi 0040B808 push edi 0040B809 lea edi,[ebp-44h] 0040B80C mov ecx,11h 0040B811 mov eax,0CCCCCCCCh 0040B816 rep stos dword ptr [edi] 9: switch (x) 10: { 0040B818 mov eax,dword ptr [ebp+8] 0040B81B mov dword ptr [ebp-4],eax 0040B81E mov ecx,dword ptr [ebp-4] 0040B821 sub ecx,64h 0040B824 mov dword ptr [ebp-4],ecx 0040B827 cmp dword ptr [ebp-4],9 0040B82B ja $L803+0Fh (0040b87b) 0040B82D mov eax,dword ptr [ebp-4] 0040B830 xor edx,edx 0040B832 mov dl,byte ptr (0040b8ad)[eax] 0040B838 jmp dword ptr [edx*4+40B899h] 11: case 100: 12: printf("100\n"); 0040B83F push offset string "100\n" (00420fa0) 0040B844 call printf (00401110) 0040B849 add esp,4 13: break; 0040B84C jmp $L803+1Ch (0040b888) 14: case 101: 15: printf("101\n"); 0040B84E push offset string "101\n" (00420f98) 0040B853 call printf (00401110) 0040B858 add esp,4 16: break; 0040B85B jmp $L803+1Ch (0040b888) 17: case 102: 18: printf("102\n"); 0040B85D push offset string "104\n" (00420024) 0040B862 call printf (00401110) 0040B867 add esp,4 19: break; 0040B86A jmp $L803+1Ch (0040b888) 20: 21: 22: case 109: 23: printf("109\n"); 0040B86C push offset string "109\n" (00420f90) 0040B871 call printf (00401110) 0040B876 add esp,4 24: break; 0040B879 jmp $L803+1Ch (0040b888) 25: default: 26: printf("Error\n"); 0040B87B push offset string "Error\n" (0042001c) 0040B880 call printf (00401110) 0040B885 add esp,4 27: break; 28: } 29: } 0040B888 pop edi 0040B889 pop esi 0040B88A pop ebx 0040B88B add esp,44h 0040B88E cmp ebp,esp 0040B890 call __chkesp (00401190) 0040B895 mov esp,ebp 0040B897 pop ebp 0040B898 ret 0040B899 aas 0040B89A mov eax,0B84E0040h 0040B89F inc eax 0040B8A0 add byte ptr [ebp-48h],bl 0040B8A3 inc eax 0040B8A4 add byte ptr [eax+edi*4+40h],ch 0040B8A8 add byte ptr [ebx-48h],bh 0040B8AB inc eax 0040B8AC add byte ptr [eax],al 0040B8AE add dword ptr [edx],eax 0040B8B0 add al,4 0040B8B2 add al,4 0040B8B4 add al,4 0040B8B6 add ecx,esp
-
对应的大表和小表
0040B899 3F B8 40 00
0040B89D 4E B8 40 00
0040B8A1 5D B8 40 00
0040B8A5 6C B8 40 00
0040B8A9 7B B8 40 00
0040B8AD 00 01 02 04 //小表
0040B8B1 04 04 04 04 //小表
0040B8B5 04 03 CC CC //小表 -
其中反汇编 0040B832 mov dl,byte ptr (0040b8ad)[eax] 含义
将小表0040B8AD加上eax的值,就是结果,而eax值此时是6,所以向后移动6位即可
0040B8AD 00 01 02 04
0040B8B1 04 04 04 0400 01 02 04 04 04 04 04 —> 向后移动6位,就是数6位,得到的结果是04,所以edx的结果是4 根据下面的公式
edx*4+40B899h 4×4+0040B899 可以得出结果是以4字节向后4位即可得到计算结果的内存地址编号:0040B8A9 7B B8 40 00
内存编号对应的内存地址:0040B87B
- 当case后的常量差距较大时,编译器仍按照if…else…进行反汇编
16. 指针
-
带*类型的变量可以加、减一个整数,但不能乘或者除.
-
带*类型变量与其他整数相加或者相减时:
带类型变量 + N = 带类型变量 + N*(去掉一个后类型的宽度)
带类型变量 - N = 带类型变量 - N(去掉一个*后类型的宽度)例:9: char** a; 10: short** b; 11: int** c; 12: 13: a = (char**)100; 0040B818 mov dword ptr [ebp-4],64h 14: b = (short**)100; 0040B81F mov dword ptr [ebp-8],64h 15: c = (int**)100; 0040B826 mov dword ptr [ebp-0Ch],64h 16: 17: 18: a++; 0040B82D mov eax,dword ptr [ebp-4] 0040B830 add eax,4 // 104 0040B833 mov dword ptr [ebp-4],eax 19: b++; 0040B836 mov ecx,dword ptr [ebp-8] 0040B839 add ecx,4 // 104 0040B83C mov dword ptr [ebp-8],ecx 20: c++; 0040B83F mov edx,dword ptr [ebp-0Ch] 0040B842 add edx,4 // 104 0040B845 mov dword ptr [ebp-0Ch],edx
- 寻找对应地址
#include<stdio.h> char data[] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09, 0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00, 0x00,0x33,0x00,0x47,0x0C,0x0E,0x00,0x0D,0x00,0x11, 0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00, 0x00,0x00,0x64,0x10,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00, 0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00, 0x00,0x02,0x74,0x0F,0x41,0x00,0x06,0x08,0x00,0x00, 0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00, 0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00 }; // type: byte num: data void search_int(int num, int length) { int i = 0; while (i < length - 4) { if (*(int*)&data[i] == num) { printf("address:%x , data:%d\n", &data[i], (int)num); } i++; } } void search_short(int num, int length) { int i = 0; while (i < length - 2) { if (*(short*)&data[i] == num) { printf("address:%x , data:%d\n", &data[i], (short)num); } i++; } } void search_char(int num, int length) { int i = 0; while (i < length - 1) { if (*(char*)&data[i] == num) { printf("address:%x , data:%d\n", &data[i], (char)num); } i++; } } void search(int type, int num) { int length = sizeof(data) / sizeof(data[0]); int i = 0; typedef void (*funcptr)(int, int); funcptr p; if (type == 4) { p = search_int; } else if (type == 2) { p = search_short; } else { p = search_char; } p(num, length); } int main(int argc, char* argv[]) { printf("data start from:%x\n", data); search(4, 0x64); return 0; }
- 再来一个吧
typedef struct TagPlayer
{
int id;
int level;
}Player;
char data[] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,
0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,
0x00,0x33,0x01,0x00,0x00,0x08,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00,
0x00,0x00,0x64,0x01,0x00,0x00,0x00,0x08,0x00,0x00,
0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,
0x00,0x02,0x57,0x4F,0x57,0x00,0x06,0x08,0x00,0x00,
0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,
0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00
};
void search_Struct()
{
int i = 0;
int length = sizeof(data) / sizeof(data[0]);
while (i < length - 8)
{
Player* temp = (Player*)&data[i];
if (temp->id == 1 && temp->level == 8) // 结构体两个int,8个字节
{
printf("address:%x id=%d, level=%d\n", &data[i], temp->id, temp->level);
}
i++;
}
}
int main(int argc, char* argv[])
{
search_Struct();
return 0;
}
void 数组指针1()
{
char (*px1)[2][3][2];
px1 = (char (*)[2][3][2])code;
printf("%d \n",sizeof(px1)); //始终是数组指针站字节宽度一直都是4字节
printf("%d \n",sizeof(*px1)); //去掉*字节宽度是1所以总宽度是1x2x3=6
printf("%x \n",*(*(*(*(px1+3)+2)+4)+2)); //1x2x3x2x3=36 1x3x2x2=12 1x2x4=8 1x2=2 --> 36+12+8+2=58 ==> 20
}
void 数组指针2()
{
int (*px)[2];
px = (int (*)[2])code;
printf("%x \n",*(*(px+0)+0)); // 这里就是首元素
printf("%x \n",*(*(px+1)+0)); // 4x2x1=8 4x0=0 8+0=8
printf("%x \n",*(*(px+2)+3)); // 4x2x2=16 4x3=12 16+12=28
}
*(p+i) = p[i]
*(*(p+i)+k) = p[i][k]
*(*(*(p+i)+k)+m) = p[i][k][m]
*(*(*(*(*(p+i)+k)+m)+w)+t) = p[i][k][m][w][t]
17. 移位