您可以使用https://godbolt.org/ 查看程序集并查看,您可以采取一些措施使其变得更加困难。首先是隐藏符号(所以checkSecret() 不是那么明显),其次是使用函数来生成密钥/密码。这个想法是,找到将导致绕过任何安全性的代码部分需要更长的时间,因此也不从 main 函数调用它可能是一个好主意。
看两种方法:
- 明确声明的密码
- 生成密码的函数
这是第一个例子:
#include <string>
#include <iostream>
using namespace std;
string secretKey = "fdasfdasfydsafhidljj3r32R#@f";
bool securityCheck(string key){
if(key==secretKey) cout << "Success!" << endl;
return key==secretKey;
}
int main(){
securityCheck(secretKey);
}
您可以在https://godbolt.org/ 上更清楚地看到,这将创建以下程序集(使用 gcc 11.2):
secretKey[abi:cxx11]:
.zero 32
.LC0:
.string "Success!"
securityCheck(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >):
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call __gnu_cxx::__enable_if<std::__is_char<char>::__value, bool>::__type std::operator==<char>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
test al, al
je .L16
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
.L16:
mov rax, QWORD PTR [rbp-8]
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call __gnu_cxx::__enable_if<std::__is_char<char>::__value, bool>::__type std::operator==<char>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
leave
ret
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 40
lea rax, [rbp-48]
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [complete object constructor]
lea rax, [rbp-48]
mov rdi, rax
call securityCheck(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
lea rax, [rbp-48]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
mov eax, 0
jmp .L22
mov rbx, rax
lea rax, [rbp-48]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
mov rax, rbx
mov rdi, rax
call _Unwind_Resume
.L22:
mov rbx, QWORD PTR [rbp-8]
leave
ret
.LC1:
.string "basic_string::_M_construct null not valid"
.LC2:
.string "fdasfdasfydsafhidljj3r32R#@f"
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
push rbx
sub rsp, 40
mov DWORD PTR [rbp-36], edi
mov DWORD PTR [rbp-40], esi
cmp DWORD PTR [rbp-36], 1
jne .L58
cmp DWORD PTR [rbp-40], 65535
jne .L58
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
lea rax, [rbp-17]
mov rdi, rax
call std::allocator<char>::allocator() [complete object constructor]
lea rax, [rbp-17]
mov rdx, rax
mov esi, OFFSET FLAT:.LC2
mov edi, OFFSET FLAT:secretKey[abi:cxx11]
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<std::allocator<char> >(char const*, std::allocator<char> const&)
lea rax, [rbp-17]
mov rdi, rax
call std::allocator<char>::~allocator() [complete object destructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov edi, OFFSET FLAT:std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
call __cxa_atexit
jmp .L58
mov rbx, rax
lea rax, [rbp-17]
mov rdi, rax
call std::allocator<char>::~allocator() [complete object destructor]
mov rax, rbx
mov rdi, rax
call _Unwind_Resume
.L58:
mov rbx, QWORD PTR [rbp-8]
leave
ret
_GLOBAL__sub_I_secretKey[abi:cxx11]:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
您可以看到secretKey 清晰可见,函数名称也是如此。
这是第二个例子:
#include <string>
#include <iostream>
using namespace std;
string getSecretKey(){
srand(100);
string chars = "qwertyuioplkjhgfdsazxcvbnm123456789";
string result = "";
for(int i = 0; i < 100; ++i){
result += chars[rand()%chars.size()];
}
return result;
}
string secretKey = getSecretKey();
bool securityCheck(string key){
if(key==secretKey) cout << "Success!" << endl;
return key==secretKey;
}
int main(){
securityCheck(secretKey);
}
这会产生以下程序集:
.LC0:
.string "qwertyuioplkjhgfdsazxcvbnm123456789"
.LC1:
.string ""
getSecretKey[abi:cxx11]():
push rbp
mov rbp, rsp
push rbx
sub rsp, 72
mov QWORD PTR [rbp-72], rdi
mov edi, 100
call srand
lea rax, [rbp-22]
mov rdi, rax
call std::allocator<char>::allocator() [complete object constructor]
lea rdx, [rbp-22]
lea rax, [rbp-64]
mov esi, OFFSET FLAT:.LC0
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<std::allocator<char> >(char const*, std::allocator<char> const&)
lea rax, [rbp-22]
mov rdi, rax
call std::allocator<char>::~allocator() [complete object destructor]
lea rax, [rbp-21]
mov rdi, rax
call std::allocator<char>::allocator() [complete object constructor]
lea rdx, [rbp-21]
mov rax, QWORD PTR [rbp-72]
mov esi, OFFSET FLAT:.LC1
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<std::allocator<char> >(char const*, std::allocator<char> const&)
lea rax, [rbp-21]
mov rdi, rax
call std::allocator<char>::~allocator() [complete object destructor]
mov DWORD PTR [rbp-20], 0
jmp .L16
.L17:
call rand
movsx rbx, eax
lea rax, [rbp-64]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::size() const
mov rcx, rax
mov rax, rbx
mov edx, 0
div rcx
mov rcx, rdx
mov rdx, rcx
lea rax, [rbp-64]
mov rsi, rdx
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator[](unsigned long)
movzx eax, BYTE PTR [rax]
movsx edx, al
mov rax, QWORD PTR [rbp-72]
mov esi, edx
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator+=(char)
add DWORD PTR [rbp-20], 1
.L16:
cmp DWORD PTR [rbp-20], 99
jle .L17
nop
lea rax, [rbp-64]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
jmp .L26
mov rbx, rax
lea rax, [rbp-22]
mov rdi, rax
call std::allocator<char>::~allocator() [complete object destructor]
mov rax, rbx
mov rdi, rax
call _Unwind_Resume
mov rbx, rax
lea rax, [rbp-21]
mov rdi, rax
call std::allocator<char>::~allocator() [complete object destructor]
jmp .L21
mov rbx, rax
mov rax, QWORD PTR [rbp-72]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
.L21:
lea rax, [rbp-64]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
mov rax, rbx
mov rdi, rax
call _Unwind_Resume
.L26:
mov rax, QWORD PTR [rbp-72]
mov rbx, QWORD PTR [rbp-8]
leave
ret
secretKey[abi:cxx11]:
.zero 32
.LC2:
.string "Success!"
securityCheck(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >):
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call __gnu_cxx::__enable_if<std::__is_char<char>::__value, bool>::__type std::operator==<char>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
test al, al
je .L28
mov esi, OFFSET FLAT:.LC2
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
.L28:
mov rax, QWORD PTR [rbp-8]
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call __gnu_cxx::__enable_if<std::__is_char<char>::__value, bool>::__type std::operator==<char>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
leave
ret
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 40
lea rax, [rbp-48]
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [complete object constructor]
lea rax, [rbp-48]
mov rdi, rax
call securityCheck(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
lea rax, [rbp-48]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
mov eax, 0
jmp .L34
mov rbx, rax
lea rax, [rbp-48]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
mov rax, rbx
mov rdi, rax
call _Unwind_Resume
.L34:
mov rbx, QWORD PTR [rbp-8]
leave
ret
.LC3:
.string "basic_string::_M_construct null not valid"
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L72
cmp DWORD PTR [rbp-8], 65535
jne .L72
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
mov eax, OFFSET FLAT:secretKey[abi:cxx11]
mov rdi, rax
call getSecretKey[abi:cxx11]()
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:secretKey[abi:cxx11]
mov edi, OFFSET FLAT:std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
call __cxa_atexit
.L72:
nop
leave
ret
_GLOBAL__sub_I_getSecretKey[abi:cxx11]():
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
您可以肯定地看到,您不再天真地扫描代码以查找有趣的字符串,而是涉及更多内容。您必须设置断点并检查不同的值,并尝试从逻辑上查看它以解决它。尽管如此,在这个例子中,符号是可见的,这会泄露大量关于应用程序正在做什么的敏感信息。从您所看到的情况来看,隐藏符号会使绕过安全性变得更加困难。