七月份,我投了YY的一份的实习生简历,由于我准备不足,有一些问题没有回答出来,下面我就将电话面试的问题总结一下,对自己不懂得问题进行了一些研究,希望也能给大家提供一些经验和帮助。
问题一:堆栈平衡的原理是什么
当问到这个问题的时候,我想到的就是堆栈平衡,但是他就问到原理是什么的时候,我就答不出来了,在结束之后我在百度下寻找,明白了其中的原理下面我就来说明一下
我的理解,其实堆栈平衡就是程序的调用。我们先来复习一下汇编的指令
call指令
1.向堆栈中压入下一行的地址,esp-4
2.jmp到call的子程序地址
ret指令
1.将esp+4,即将堆栈中的值出栈
2.jmp dword ptr ds:[esp-4],即jmp到当前的位置
知道了基础之后,我们就可以通过一个例子进行描述,我选择了一个upx的壳,壳的入口点的各个寄存器的值,pushad将各个寄存器的值压入栈中
<pre name="code" class="plain">EAX 00000000
ECX 0012FFB0
EDX 7C92E4F4 ntdll.KiFastSystemCallRet
EBX 7FFD3000
ESP 0012FFC4
EBP 0012FFF0
ESI 0012B5D0
EDI 005A7908
EIP 0040E8C0 UPX.<ModuleEntryPoint>
C 0 ES 0023 32位 0(FFFFFFFF)
P 1 CS 001B 32位 0(FFFFFFFF)
A 0 SS 0023 32位 0(FFFFFFFF)
Z 1 DS 0023 32位 0(FFFFFFFF)
S 0 FS 003B 32位 7FFDF000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_NO_IMPERSONATION_TOK
到达OEP时各个寄存器的变化
<pre name="code" class="plain">EAX 00000000
ECX 0012FFB0
EDX 7C92E4F4 ntdll.KiFastSystemCallRet
EBX 7FFD3000
ESP 0012FFC4
EBP 0012FFF0
ESI 0012B5D0
EDI 005A7908
EIP 004010CC UPX.004010CC
C 0 ES 0023 32位 0(FFFFFFFF)
P 1 CS 001B 32位 0(FFFFFFFF)
A 0 SS 0023 32位 0(FFFFFFFF)
Z 1 DS 0023 32位 0(FFFFFFFF)
S 0 FS 003B 32位 7FFDF000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_NO_IMPERSONATION_TOK
我们发现是不是除了eax,其他的值都一样,因为eax保存了当前oep的值,所以eax有变化
pushed
0040E8C0 > 60 pushad
0040E8C1 BE 15B04000 mov esi,UPX.0040B015
将各个寄存器的数据压栈,其结果如下:
<pre name="code" class="plain">EDI 005A7908
ESI 0012B5D0
EBP 0012FFF0
ESP 0012FFC4
EBX 7FFD3000
EDX 7C92E4F4
ECX 0012FFB0
EAX 00000000
popad
0040E8C0 > 60 pushad
0040E8C1 BE 15B04000 mov esi,UPX.0040B015
结果就是到达oep时的各个寄存器的值
这个时候我想大家就明白了,其实ESP定律就是完成了一次调用子程序的过程。在这里关键的地方是:如果我们要返回父程序,则当我们在堆栈中进行堆栈的操作的时候,一定要保证在RET这条指令之前,ESP指向的是我们压入栈中的地址
问题二:全局变量在堆栈中的变化
下面这段代码是我在VC++6.0进行调试时进入反汇编代码是遇到的,我发现不管什么样的函数,都有这样的一段代码,上网查询发现他是函数框架,也就说这个框架是函数执行时必须的
esp指向的地方就是堆栈
00401020 push ebp /ESP-4,并将ebp的值写入esp内存中
00401021 mov ebp,esp /esp的值付给ebp
00401023 sub esp,40h /开辟一个40h大的空间
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h /循环10次
00401031 mov eax,0CCCCCCCCh /eax的值为0xCCCCCCCCh
00401036 rep stos dword ptr [edi] /ESP+4,重复将EAX的值付给edi的内存中
8: return 0;
00401038 xor eax,eax /eax清零
9: }
0040103A pop edi
0040103B pop esi
0040103C pop ebx
0040103D mov esp,ebp
0040103F pop ebp
00401040 ret
#include "stdafx.h"
int fun()
{
return 0;
}
int main(int argc, char* argv[])
{
fun();
return 0;
}
我们画出他的堆栈变化示意图:
0018FEA0 40 FF 18 00 @... /push ebx
0018FEA4 80 10 40 00 ..@. /push esi
0018FEA8 00 E0 FD 7F .帻. /push edi
0018FEAC CC CC CC CC 烫烫
0018FEB0 CC CC CC CC 烫烫
0018FEB4 CC CC CC CC 烫烫
0018FEB8 CC CC CC CC 烫烫
0018FEBC CC CC CC CC 烫烫
0018FEC0 CC CC CC CC 烫烫
0018FEC4 CC CC CC CC 烫烫
0018FEC8 CC CC CC CC 烫烫
0018FECC CC CC CC CC 烫烫
0018FED0 CC CC CC CC 烫烫
0018FED4 CC CC CC CC 烫烫
0018FED8 CC CC CC CC 烫烫
0018FEDC CC CC CC CC 烫烫
0018FEE0 CC CC CC CC 烫烫
0018FEE4 CC CC CC CC 烫烫
0018FEE8 CC CC CC CC 烫烫
0018FEEC 40 FF 18 00 @... /push ebp
我们会发现这是一个循环的过程,esp-40h开辟出一个空间,eax的值将所开辟的内存值都赋值0xCCCCCCCC,这个想明白的话,那变量是如何变换的也就简单了,我们只需要在函数中赋值相应的变量,参数或者表达式就可以了。
#include "stdafx.h"
int fun2(int a,int b)
{
return a+b;
}
int fun1(int a,int b)
{
fun2(5,6);
return a+b;
}
int fun(int a,int b)
{
int r;
r=a+b;
fun1(3,4);
return 0;
}
int main(int argc, char* argv[])
{
fun(1,2);
return 0;
}
VC++6.0部分的汇编代码:
15: int fun(int a,int b)
16: {
004010B0 push ebp
004010B1 mov ebp,esp
004010B3 sub esp,44h
004010B6 push ebx
004010B7 push esi
004010B8 push edi
004010B9 lea edi,[ebp-44h]
004010BC mov ecx,11h
004010C1 mov eax,0CCCCCCCCh
004010C6 rep stos dword ptr [edi]
17: int r;
18: r=a+b;
004010C8 mov eax,dword ptr [ebp+8]
004010CB add eax,dword ptr [ebp+0Ch]
004010CE mov dword ptr [ebp-4],eax
19: fun1(3,4);
004010D1 push 4
004010D3 push 3
004010D5 call @ILT+5(fun1) (0040100a)
004010DA add esp,8
20: return 0;
004010DD xor eax,eax
我们在VC中进入调试就会发现一个重要的变化
ebp-4的位置为第一个变量的位置,ebp+8的位置为第一个参数的位置,大家可以自己画一下堆栈图,而且每一个函数都和我上面所说框架的相同,而且是循环的。而我以前从来没有注意到

当然了问题不只是这两个,还有对软件破解的流程,脱壳的方法,以及用c语言编写过那些东西等等。我的编程能力并不是特别的好,所以在c语言上的问题也没有回答好。这次的面试也给我很大的启发 1.学习要注重深度,而非广度,原理性的知识要弄懂,不可一知半解。2.复习c语言,对c语言不仅要会用,这要理解函数背后的运行原理。所以接下来还要继续努力。