进入本次正题:
(八)[ 标准C++ 之 内置类型--容器 ]
今次来看看标准C++内置类型之vector是个什么样子:
先看Debug版本的,等下再看Release的.
我就直接贴代码了.
还是把cpp代码贴上:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//容器
void fuck_vector()
{
int i =0;
vector< int > ivec(4);
__asm nop
__asm nop
__asm nop
__asm nop
printf("sizeof ivec %d \n",sizeof(ivec));
ivec[2] += 2;
i += ivec[2];
}
//函数模板
template <class T>
T t_func(T a ,T b)
{
return a < b ? a : b ;
}
//类模板
template<class T>
class A
{
public:
A();
A(T _a,T _b);
T sum();
private:
T a;
T b;
};
template <class T>
A<T>::A()
{
a=0;b=0;
}
template<class T>
A<T>::A(T _a,T _b)
{
a=_a;b=_b;
}
template<class T>
T A<T>::sum()
{
return (a+b);
}
int main(int argc, char *argv[], char **env)
{
int i =0;
__asm int 3
fuck_vector(); //容器测试
i = t_func(2.2,3.3); //函数模板测试
A<int> ai(3,4); //
A<double> ad(3.1,4.0); // 类模板测试
cout<<ai.sum()<<" "<<ad.sum()<<endl; //
return 0;
}
--D:\Reverse\C++\Lesson8\Lesson8.cpp --------------------------------------------------------------
1: #include <iostream>
2: #include <string>
3: #include <vector>
4: using namespace std;
5:
6: //容器
7: void fuck_vector()
8: {
00401650 55 push ebp
00401651 8B EC mov ebp,esp
00401653 6A FF push 0FFh
00401655 68 79 87 44 00 push offset __ehhandler$?fuck_vector@@YAXXZ (00448779)
0040165A 64 A1 00 00 00 00 mov eax,fs:[00000000]
00401660 50 push eax
00401661 64 89 25 00 00 00 00 mov dword ptr fs:[0],esp
//从这行向上数5句是按SHE的.所以我们知道使用vector的call体的内部是要被编译器 SHE的.
00401668 83 EC 60 sub esp,60h
0040166B 53 push ebx
0040166C 56 push esi
0040166D 57 push edi
0040166E 8D 7D 94 lea edi,[ebp-6Ch]
00401671 B9 18 00 00 00 mov ecx,18h
00401676 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040167B F3 AB rep stos dword ptr [edi]
: int i =0;
0040167D C7 45 F0 00 00 00 00 mov dword ptr [ebp-10h],0
//因为上面装SHE使得栈抬高了0ch.
0: vector< int > ivec(4);
0401684 C7 45 D8 00 00 00 00 mov dword ptr [ebp-28h],0 //如果我们把i作为var1, 则这里可以假定为var7. var7下面除了var1外还有var2-6 5个DWORD值. 这里它要干什么我们先不管.
0040168B 8D 45 DC lea eax,[ebp-24h]
0040168E 50 push eax //将var6的地址入栈.
0040168F 8D 4D D8 lea ecx,[ebp-28h]
00401692 51 push ecx //var7的地址入栈
00401693 6A 04 push 4 //vector的元素个数入栈
00401695 8D 4D E0 lea ecx,[ebp-20h] //这里有点像this指针. 不过vector的内存是在堆上分配的, 所以我猜这里的一段栈内存只是vector取数据后存放的一个buffer, Release版本的应该不会这样, 先接着看吧.
00401698 E8 E8 FB FF FF call @ILT+640(std::vector<int,std::allocator<int>
>::vector<int,std::allocator<int> >) (00401 //调用构造函数,智能的分配内存.
0040169D C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0 //把上面那个压进去的SHE的标志置零. 告诉SHE一些信息 . :D
1: ivec[2] += 2; //容器的取数.
004016A4 6A 02 push 2 //下标拿出来.
004016A6 8D 4D E0 lea ecx,[ebp-20h] //var5的地址入栈. (记得var1是 i 啊 , 那么你想 var 2,3,4,5 正好4个DWORD了. )
004016A9 E8 14 FB FF FF call @ILT+445(std::vector<int,std::allocator<int> >::operator[]) (004011c2) //vector这里是重载了[]运算符. 取数. 在这里它取出的不是vector[2]的数值, 而是其地址.
004016AE 89 45 D4 mov dword ptr [ebp-2Ch],eax //var8保存地址
004016B1 8B 55 D4 mov edx,dword ptr [ebp-2Ch] //再用edx保存地址; 这里你就可以体会到Debug版本的啰嗦了. 不过有时用debug版本看思路会清晰些.
004016B4 8B 02 mov eax,dword ptr [edx]
004016B6 83 C0 02 add eax,2
004016B9 8B 4D D4 mov ecx,dword ptr [ebp-2Ch] //地址又放到ecx.
004016BC 89 01 mov dword ptr [ecx],eax //这四句完成对ivec的加2操作.
2: i += ivec[2]; //这句跟上面差不多, 到这里我发现, 上面提到的var2-5 并没有被使用, 那个ecx也不是this指针. 可能是Debug版本的原因吧, 到了Release,就不会有这个问题了.
004016BE 6A 02 push 2
004016C0 8D 4D E0 lea ecx,[ebp-20h]
004016C3 E8 FA FA FF FF call @ILT+445(std::vector<int,std::allocator<int> >::operator[]) (004011c2)
004016C8 8B 55 F0 mov edx,dword ptr [ebp-10h]
004016CB 03 10 add edx,dword ptr [eax]
004016CD 89 55 F0 mov dword ptr [ebp-10h],edx
3: }
004016D0 C7 45 FC FF FF FF FF mov dword ptr [ebp-4],0FFFFFFFFh //是SHE相关的信息处理. 善后处理吧.
004016D7 8D 4D E0 lea ecx,[ebp-20h]
004016DA E8 0F FC FF FF call @ILT+745(std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> >) (0040 //析构对象 , 看这里是不是觉得ecx特像this指针, 后面告诉你答案.
004016DF 8B 4D F4 mov ecx,dword ptr [ebp-0Ch]
004016E2 64 89 0D 00 00 00 00 mov dword ptr fs:[0],ecx
004016E9 5F pop edi
004016EA 5E pop esi
004016EB 5B pop ebx
004016EC 83 C4 6C add esp,6Ch
004016EF 3B EC cmp ebp,esp
004016F1 E8 CA 01 02 00 call __chkesp (004218c0)
004016F6 8B E5 mov esp,ebp
004016F8 5D pop ebp
004016F9 C3 ret
Debug版本容器小结: 刚才大致跟了下, 发现上面几处说到的ecx 中并不是存放this指针的, 真正存了ivec这个对象的指针的是DWORD ptr [ecx+4] (= 30xxxxxx, 一个堆上的地址.) , 所以我觉得debug版本的,用于直接在VC6的界面中看Dasm有时还是行的(它符号齐全), 不过要真分析起来, 还是Release版本的跟实际接近些. 下面看看Release版本的吧.
////Release:
//main调用fuck_vector()
00401050 /$ 56 push esi ; 这里为什么要这么压栈了?我想应该是为了开辟8个字节的局部变量空间.(这里是main函数,进来就直接两个push,优化的挺厉害的.)
00401051 |. 57 push edi ; 如上.
00401052 |. E8 A9FFFFFF call Lesson8.00401000
//进来这个子呼叫.
00401000 /$ 55 push ebp
00401001 |. 8BEC mov ebp, esp
00401003 |. 83EC 10 sub esp, 10
00401006 |. 6A 10 push 10 ; 容器中4个int 所以压入大小16
00401008 |. E8 1C520000 call Lesson8.00406229 ; 初始化这个容器.
0040100D |. 83C4 04 add esp, 4 ; 没想到容器初始化竟是C调用, 没错吧这里?
00401010 |. 8BC8 mov ecx, eax ; 刚才那个函数返回值为容器地址.在堆上.
00401012 |. BA 04000000 mov edx, 4 ; 容器中个数.
00401017 |> 85C9 /test ecx, ecx
00401019 |. 74 06 |je short Lesson8.00401021 ; 堆申请失败则玩完
0040101B |. C701 00000000 |mov dword ptr [ecx], 0
00401021 |> 83C1 04 |add ecx, 4
00401024 |. 4A |dec edx
00401025 |.^ 75 F0 \jnz short Lesson8.00401017 ; 多谢,容器帮我们将元素初始化为零
00401027 |. 8D48 10 lea ecx, [eax+10]
0040102A |. 894D F8 mov [local.2], ecx ; var2 = &ivec[4] ? 这里不太理解.,
0040102D |. 90 nop
0040102E |. 90 nop
0040102F |. 90 nop
00401030 |. 90 nop
00401031 |. 8B50 08 mov edx, [eax+8] ; edx = ivec[2]
00401034 |. 50 push eax ; 为析构函数压参, 跟下面这句可没关系.
00401035 |. 83C2 02 add edx, 2
00401038 |. 8950 08 mov [eax+8], edx ; ivec[2] += 2
0040103B |. E8 00290000 call Lesson8.00403940 ; 容器的析构函数.(跟进去会有VirtualFree)
00401040 |. 83C4 04 add esp, 4 ; 从这也可知析构函数为C调用且一个参数.
00401043 |. 8BE5 mov esp, ebp
00401045 |. 5D pop ebp
00401046 \. C3 ret
小结: 其实这从没对容器的构造及析构函数好好的跟踪, 有时间仔细的跟跟, 这次也只是有个大概了解. 其实在逆向中, 知道这些也就够了, 因为现在的工具太好用了, 已经可以让OD自动把string 啊 vector啊什么的构造函数或者重载的运算符自己显示出来了.
下一Lesson 再分析下模板. 今天就到这吧.
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)