cpp逆向之旅2

string的简单使用

#include <iostream>
#include <string>

using std::string;
using std::cout; using std::endl;

int main(void)
{
	string s = "abcd";
	for (auto &c : s)
		c = toupper(c);
	cout << s << endl;
	return 0;
}

来看第一句

string s = 'abcd';

mscv2015反汇编 32位

.text:0040102E                 lea     ecx, [ebp+Memory] ; Dst
.text:00401031                 mov     [ebp+var_18], 0
.text:00401038                 mov     [ebp+var_14], 0Fh
.text:0040103F                 mov     byte ptr [ebp+Memory], 0
.text:00401043                 call    sub_401180

clang+llvm反汇编 64位

.text:0000000000400CE2                 lea     rax, [rbp+var_30]
.text:0000000000400CE6                 mov     rdi, rax
.text:0000000000400CE9                 mov     [rbp+var_68], rax
.text:0000000000400CED                 call    __ZNSaIcEC1Ev   ; std::allocator<char>::allocator(void)
.text:0000000000400CF2                 mov     ecx, offset aAbcd ; "abcd"
.text:0000000000400CF7                 mov     esi, ecx
.text:0000000000400CF9                 lea     rdi, [rbp+var_28]
.text:0000000000400CFD                 mov     rdx, [rbp+var_68]
.text:0000000000400D01                 call    __ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_ ; std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(char const*,std::allocator<char> const&)
.text:0000000000400D06                 jmp     $+5
.text:0000000000400D0B
.text:0000000000400D0B loc_400D0B:                             
.text:0000000000400D0B                 lea     rdi, [rbp+var_30]
.text:0000000000400D0F                 call    __ZNSaIcED1Ev   ; std::allocator<char>::~allocator()

可以看到,mscv编译器中,主要调用了sub_401180函数,C++在定义变量时有直接初始化和拷贝初始化两种方式,这里使用的是拷贝初始化,所以这个函数应该是拷贝构造函数。函数内部比较复杂。。不过可以明显看到几处关键部分

.text:0040124C push    edi
.text:0040124D push    offset aAbcd    ; "abcd"
.text:00401252 push    eax             ; Dst
.text:00401253 mov     [ebp+var_4], eax
.text:00401256 mov     [esi+10h], edi
.text:00401259 mov     [esi+14h], ebx
.text:0040125C call    memcpy
······
.text:004011A1 push    edi
.text:004011A2 push    offset aAbcd    ; "abcd"
.text:004011A7 push    ebx             ; Dst
.text:004011A8 mov     [esi+10h], edi
.text:004011AB call    memmove

当使用直接初始化时,我们实际上时要求编译器选择普通的函数中与我们提供的参数最匹配的构造函数;当使用拷贝初始化时,我们要求编译器将运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换。拷贝初始化通过拷贝构造函数和移动构造函数来完成。

拷贝初始化发生时机

  • 使用 = 定义变量时
  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类的成员

拷贝构造函数的第一个参数必须为引用的原因:
当我们调用一个函数时,若函数参数并非引用类型,则会调用拷贝构造函数来传参,若拷贝构造函数的参数也不是引用,那么就会无限调用自身为自己传值。

而在clang+llvm中,则是直接使用了

std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string

其声明如下

basic_string(
    const basic_string& right,
    size_type _Roff,
    size_type count = npos);

所以本质上还是使用的拷贝构造函数

来看接下来一段代码

for (auto &c : s)
		c = toupper(c);

mscv2015反汇编 32位

.text:00401055                 lea     esi, [ebp+input]
.text:00401058                 cmp     ecx, 10h
.text:0040105B                 cmovnb  eax, [ebp+input]
.text:0040105F                 cmovnb  esi, [ebp+input]
.text:00401063                 xor     edx, edx
.text:00401065                 add     eax, [ebp+var_18]
.text:00401068                 xor     edi, edi
.text:0040106A                 mov     ebx, eax
.text:0040106C                 sub     ebx, esi
.text:0040106E                 cmp     esi, eax
.text:00401070                 cmova   ebx, edx
.text:00401073                 test    ebx, ebx
.text:00401075                 jz      short loc_40109A
.text:00401077                 nop     word ptr [eax+eax+00000000h]
.text:00401080
.text:00401080 loc_401080:                             ; CODE XREF: sub_401000+95↓j
.text:00401080                 movsx   eax, byte ptr [esi]
.text:00401083                 push    eax             ; C
.text:00401084                 call    ds:toupper
.text:0040108A                 inc     edi
.text:0040108B                 mov     [esi], al
.text:0040108D                 add     esp, 4
.text:00401090                 lea     esi, [esi+1]
.text:00401093                 cmp     edi, ebx
.text:00401095                 jnz     short loc_401080

clang+llvm反汇编 64位

.text:0000000000400D20                 call    __ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5beginEv ; std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(void)
.text:0000000000400D25                 mov     [rbp+var_50], rax
.text:0000000000400D29                 mov     rdi, [rbp+var_48]
.text:0000000000400D2D                 call    __ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE3endEv ; std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(void)
.text:0000000000400D32                 mov     [rbp+var_58], rax
.text:0000000000400D36
.text:0000000000400D36 loc_400D36:                             ; CODE XREF: main+AE↓j
.text:0000000000400D36                 lea     rdi, [rbp+var_50]
.text:0000000000400D3A                 lea     rsi, [rbp+var_58]
.text:0000000000400D3E                 call    sub_400E10
.text:0000000000400D43                 test    al, 1
.text:0000000000400D45                 jnz     loc_400D50
.text:0000000000400D4B                 jmp     loc_400D9A
.text:0000000000400D50 ; ---------------------------------------------------------------------------
.text:0000000000400D50
.text:0000000000400D50 loc_400D50:                             ; CODE XREF: main+75↑j
.text:0000000000400D50                 lea     rdi, [rbp+var_50]
.text:0000000000400D54                 call    sub_400E50
.text:0000000000400D59                 mov     [rbp+var_60], rax
.text:0000000000400D5D                 mov     rax, [rbp+var_60]
.text:0000000000400D61                 movsx   edi, byte ptr [rax] ; c
.text:0000000000400D64                 call    _toupper
.text:0000000000400D69                 mov     cl, al
.text:0000000000400D6B                 mov     rdx, [rbp+var_60]
.text:0000000000400D6F                 mov     [rdx], cl
.text:0000000000400D71                 lea     rdi, [rbp+var_50]
.text:0000000000400D75                 call    sub_400E70
.text:0000000000400D7A                 mov     [rbp+var_70], rax
.text:0000000000400D7E                 jmp     loc_400D36

可以看到,mscv2015的编译结果在循环中完全是内存操作,引用和指针在汇编级别几乎没有区别;而clang+llvm则是使用了begin和end两个迭代器的东西来遍历字符串,虽然本质上它也只是返回内存地址。
关于这个循环结构可以看到,在mscv编译器中,for循环被优化成了if语句+do while循环的模式,而后者则看起来更像一个while循环。

到此结束了。

猜你喜欢

转载自blog.csdn.net/qq_35713009/article/details/85225891
今日推荐