52 Stimmen

Wie kann man geheime Schlüssel in einem Code verstecken?

Ich habe mich schon seit einiger Zeit gefragt, wie manche Software geheime Schlüssel so versteckt, dass sie nicht auf triviale Weise entdeckt werden können. Nur ein paar Beispiele:

  • DVD-Player-Software versteckt CSS-Tasten
  • Software mit Seriennummern/Registrierungscodes verbirgt Schlüssel/Hashes, die zur Validierung der Seriennummern verwendet werden

Offensichtlich tun diese Programme mehr, als nur den Schlüssel in einem Byte[] zu speichern, denn dann wäre es ein Leichtes, ihre Schlüssel zu stehlen und eigene Seriennummern zu generieren usw.

Welche Strategien werden angewandt, um diese Schlüssel so zu verstecken, dass sie nicht leicht gefunden werden können?

1voto

Yakov Fain Punkte 11164

Als wir mit der Entwicklung unserer Software begonnen haben, haben wir eine veraltete Lizenzdatei erstellt. Dann stellten wir fest, dass nicht allzu viele Leute am Kauf unserer Software interessiert sind. Dann haben wir beschlossen, sie kostenlos zu verschenken. Viel mehr Leute begannen, sich dafür zu interessieren, unser Meisterwerk zumindest auszuprobieren. Schließlich haben wir unsere Software als Open Source zur Verfügung gestellt. Sehr viel mehr Benutzer begannen, sie zu benutzen. Jetzt hoffen wir nur noch, dass eine kleine Anzahl dieser Benutzer zu zahlenden Kunden wird (d.h., dass sie produktiven Support kaufen oder Anpassungen wünschen).

Fazit: Wenn jemand Ihre Software knacken will, wird er/sie es sowieso tun. Lohnt es sich wirklich, Ihre Zeit mit dem Versuch zu verschwenden, sie mit diesem versteckten geheimen Schlüssel zu schützen?

1voto

galinette Punkte 8254

Wenn Sie es sich leisten können, ist es am besten, den privaten Schlüssel in einem kryptografischen USB-Token zu speichern. Der Schlüssel ist schreibgeschützt, d. h. Sie können ihn setzen, aber nicht lesen. Der Token führt die kryptografischen Operationen intern, in seiner Hardware, durch. Es wird sehr kompliziert, den Schlüssel abzurufen (wenn der Token keine bekannte Sicherheitslücke aufweist, was bei älteren Token nicht der Fall ist).

1voto

JVE999 Punkte 3066

Sie können verwenden https://godbolt.org/ Es gibt ein paar Dinge, die Sie tun können, um die Montage zu erschweren. Erstens kann man die Symbole ausblenden (also checkSecret() ist nicht so offensichtlich), und die zweite, eine Funktion zu verwenden, um den Schlüssel/das Kennwort zu generieren. Der Gedanke dahinter ist, dass es länger dauert, den Teil des Codes zu finden, der zur Umgehung der Sicherheit führt, so dass auch kein Aufruf aus der main Funktion ist wahrscheinlich eine gute Idee.

Wir betrachten zwei Ansätze:

  1. ein ausdrücklich angegebenes Passwort
  2. eine Funktion, die das Passwort generiert

Hier ist ein Beispiel für die erste:

#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);
}

Wie Sie auf der folgenden Seite noch deutlicher sehen können https://godbolt.org/ erzeugt dies die folgende Baugruppe (mit 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

Sie können sehen, dass die secretKey ist deutlich sichtbar, und die Funktionsnamen sind es auch.

Hier ist ein zweites Beispiel:

#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);
}

Das Ergebnis ist die folgende Baugruppe:

.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

Sie sehen also, dass Sie den Code nicht mehr naiv nach interessanten Zeichenketten durchsuchen müssen, aber es geht um mehr. Sie müssen Haltepunkte setzen und verschiedene Werte überprüfen und versuchen, den Code logisch zu durchschauen, um ihn zu lösen. In diesem Beispiel sind die Symbole jedoch sichtbar, was eine Menge sensibler Informationen darüber verrät, was die Anwendung tut. Durch das Ausblenden der Symbole wird die Umgehung der Sicherheit schwieriger, wie Sie sehen können.

0voto

Adam Berent Punkte 1992

Geheime Schlüssel in einem Code zu verstecken, ist nicht wirklich sicher. Wie Sie vielleicht schon bemerkt haben, werden DVDs und die meisten Software-Seriennummernregistrierungen täglich gehackt. Wenn Sie etwas wirklich sicher machen wollen, müssen Sie die Verschlüsselung mit öffentlichen Schlüsseln verwenden.

0voto

FNG Punkte 153

Das geht nicht, das ist unmöglich. Jeder Angreifer, der Zugriff auf den Zielcomputer hat, wäre in der Lage, Ihren Code zu disassemblieren, um ihn zu finden, oder die Schlüsseldatei auf dem Zielcomputer zu finden, usw.

Die einzige Möglichkeit, die Sicherheit des Verschlüsselungsschlüssels zu gewährleisten, besteht darin, dass der Benutzer ihn bei Bedarf manuell eingibt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X