Sberbank oder hin und zurück



KAPITEL 1. Unerwartete Gäste


Alles begann an diesem unglücklichen Morgen, als der Projektmanager ankündigte, dass die Fristen für die Projektumsetzung schnell und entscheidend um einen Monat verkürzt werden sollten. Genauer gesagt sollte das Projekt in 4 Tagen fertig sein. Nein, unser PO ist kein Biest und sieht überhaupt nicht aus wie eine Eule (vielleicht nur ein bisschen wie eine Krähe), es ist einfach passiert. Nun, wenn es notwendig ist, ist es notwendig, zumal dem Team (und ich bin der führende Entwickler des "C" -Teams) etwas Leckeres versprochen wurde. Die Uhr und der Kalender waren Donnerstag, 11:00 Uhr, bis Montag sollte das Projekt fertig sein.

Was machen wir überhaupt? Wir beschäftigen uns mit der Automatisierung von Kinos - automatische und Fernsteuerung von Geräten, Automatisierung von Filmvorführungen, Überwachung, Videotafeln und jetzt auch Terminals für den Verkauf von Tickets und Bars. Insbesondere ist der letzte Absatz diesem Artikel gewidmet.

Das Projekt selbst, das vor Montag abgeschlossen werden musste, ist eine Art Schicht zwischen dem Hauptserver von Scala und dem Eisenzahlungsterminal VeriFone VX 820 (tatsächlich gibt es mehr Terminals, aber nehmen Sie es nur als Beispiel). Es ist klar, dass uns niemand einfach so Transaktionen geben wird, daher werden die Dienstprogramme und Bibliotheken von Sberbank / Arcus und UCS verwendet. Daher sollte das Arbeitsschema wie folgt aussehen:



Äußerlich sieht es so aus:



Dieses Subsystem sollte auch für Standardkassen verwendet werden, die jeder in einem Kino an der Kasse gesehen hat.

Nach der internen Tradition nennen wir jedes Projekt unseres Teams einen Namen aus der altnordischen Mythologie. Für dieses Subsystem wurde der Name Gefjon gewählt - der Name der Göttin der Fruchtbarkeit und Fruchtbarkeit (ein guter Name für einen Zahlungsserver, nicht wahr? Nun, die Legende über die Bullen, die die Insel abschneiden, passt ideal zur aktuellen Architektur. Unterbrechen der Arbeit mit Geräten aus einer Hochsprache).

Das Format für eingehende und ausgehende Nachrichten ist ein HTTP-Server mit einer JSON-Last. Dies ist der optimale Kompromiss zwischen Scala, bei dem es schwierig ist, Binärdaten von Socket-Streams zu isolieren, und C, bei dem es schwierig ist, Objekte über das Netzwerk zu übertragen. Es gibt nicht viele mögliche Vorgänge, die ausgeführt werden müssen: Zahlung, Stornierung, Rückgabe, verschiedene Arten von Berichten, Öffnen des Servicemenüs und Ping. Es sieht nicht kompliziert aus. Da es drei Bankensysteme gibt (und eine spätere Wiederauffüllung der Familie erwartet wird), wurde beschlossen, das Projekt in Komponenten aufzuteilen:



Die grünen Blöcke sind diejenigen, die wir machen mussten, die blauen sind diejenigen, die nicht geändert werden können und die die Bank bereitstellt.

Da die Hauptprobleme nur bei Software von Sberbank auftraten, wird der gesamte Artikel Fallstricken gewidmet sein, die wir mit unserem Boot gezählt haben.

KAPITEL 2. Lammbraten



(Foto: heaclub.ru)

... sieht ungefähr so ​​aus. Der Code dieses Prototyps, der vor einigen Monaten geschrieben wurde, um allen übergeordneten Personen klar zu machen, dass wir mit Bankanwendungen arbeiten können, sah ungefähr gleich aus.

char buf[BUF_KB * 2]; char * null; char * grep; #ifdef _WIN32_WINNT char * ptr; null = "nul"; grep = "findstr"; #else null = "/dev/null"; grep = "grep"; #endif sprintf(buf, "%s %"PRIi32"= %sops.ini >%s 2>%s || " "echo %"PRIi32"=9,6,PINPAD_TEST >> %sops.ini", grep, TERM_ARCUS_TEST_PINPAD, TERM_PATH, null, null, TERM_ARCUS_TEST_PINPAD, TERM_PATH); #ifdef _WIN32_WINNT ptr = buf; while (*ptr) { if (*ptr == '/') *ptr = '\\'; ptr++; } #endif 

Es ist klar, dass dies nicht für die Produktionsversion geeignet war, daher war es im Wesentlichen notwendig, alles neu zu schreiben.

Jede Bank, die Bibliotheken für die Arbeit mit dem Terminal bereitstellt, bietet normalerweise zwei Verbindungsoptionen: über die Bibliotheksfunktionen (.so / .dll) oder über ein vorgefertigtes Dienstprogramm, das nur zwei Werte übergeben muss - die Art der Operation und den Betrag (falls erforderlich). Theoretisch nichts Kompliziertes, nur etwas

 char buffer[100]; sprintf(buffer, "%d %d", atoi(argv[1]), atoi(argv[2])); system(buffer); 

Das Ergebnis der Operation wird in die Datei "e" und die Belegprüfung in die Datei "p" gestellt. Senden Sie diese Dateien einfach mit der Konvertierung in JSON an stdout, damit der HTTP-Server sie nur als Nutzdaten sendet, ohne darüber nachzudenken, was dort vorhanden ist.

Aber dieser Artikel wäre nicht veröffentlicht worden, wenn alles so einfach gewesen wäre.

KAPITEL 4. Über den Berg und unter den Berg


Die ursprüngliche Version der Implementierung war ein einfacher Anwendungsaufruf - der HTTP-Server rief den erforderlichen Wrapper mit einheitlichen Parametern auf (z. B. der X-Report ist 4), und das Dienstprogramm, zum Beispiel gfj_pilot, startete sb_pilot mit dem Parameter, der für diesen Vorgang erforderlich war (zum Beispiel ist der X-Report 9). . Anschließend las das Wrapper-Dienstprogramm das Ergebnis der Operation aus der E-Datei (z. B. 2000 - "Zahlung abgelehnt, Vorgang wiederholen") und wandelte es in einen universellen Fehler um (z. B. 3 - "Fehler beim Lesen oder Verarbeiten der Karte / des Kontos, Wiederholen des Vorgangs"). Danach wurde die "p" -Datei in base64 konvertiert, um eine Unterbrechung der Formatierung zu vermeiden, und zusammen mit dem Ergebnis als JSON an stdout gesendet.

All dies funktionierte perfekt, bis wir in einem schönen Moment darüber informiert wurden, dass ...

... das funktioniert unter Windows nicht.



Genauer gesagt hat Windows selbst keine Probleme (außer dass der Beleg in Cp-1251-Codierung generiert wird und die Konsole in CP866 funktioniert). Die "e" -Datei wurde einfach nicht generiert. Ein Bankdienstprogramm direkt gestartet:

 C:\banks\sber\sb_pilot>dir    C   .   : B401-6B9D   C:\banks\sber\sb_pilot 04.02.2019 12:28 <DIR> . 04.02.2019 12:28 <DIR> .. 31.01.2019 17:12 10 832 F12X24.BIN 31.01.2019 17:12 128 000 gate.dll 31.01.2019 17:12 72 192 loadparm.exe 31.01.2019 17:12 36 204 OPT0.R 31.01.2019 17:12 20 716 OPT1.R 31.01.2019 17:12 1 806 OPT3.R 31.01.2019 17:12 388 608 pilot_nt.dll 31.01.2019 23:06 463 pinpad.ini 31.01.2019 17:12 91 136 posScheduler.exe 31.01.2019 17:12 418 printers.ini 01.02.2019 16:51 91 646 sbkernel1902.log 31.01.2019 17:12 653 312 sbrf.dll 31.01.2019 17:12 840 192 SBRFCOM.dll 31.01.2019 17:12 3 142 656 sb_kernel.dll 01.02.2019 16:51 9 SESS.D 01.02.2019 16:51 715 SPLC.D 31.01.2019 17:12 72 192 upwin.exe 20  5 659 718  2  37 567 004 672   #    (1)  10  (1000 ) C:\banks\sber\sb_pilot>loadparm.exe 1 1000 C:\banks\sber\sb_pilot>dir    C   .   : B401-6B9D   C:\banks\sber\sb_pilot 04.02.2019 12:28 <DIR> . 04.02.2019 12:28 <DIR> .. 04.02.2019 12:28 216 commerr.log 31.01.2019 17:12 10 832 F12X24.BIN 31.01.2019 17:12 128 000 gate.dll 31.01.2019 17:12 72 192 loadparm.exe 31.01.2019 17:12 36 204 OPT0.R 31.01.2019 17:12 20 716 OPT1.R 31.01.2019 17:12 1 806 OPT3.R 01.02.2019 18:51 1 349 p 31.01.2019 17:12 388 608 pilot_nt.dll 31.01.2019 23:06 463 pinpad.ini 31.01.2019 17:12 91 136 posScheduler.exe 31.01.2019 17:12 418 printers.ini 04.02.2019 12:28 92 218 sbkernel1902.log 31.01.2019 17:12 653 312 sbrf.dll 31.01.2019 17:12 840 192 SBRFCOM.dll 31.01.2019 17:12 3 142 656 sb_kernel.dll 01.02.2019 16:51 9 SESS.D 01.02.2019 16:51 715 SPLC.D 31.01.2019 17:12 72 192 upwin.exe 19  5 659 029  2  37 567 008 768   C:\banks\sber\sb_pilot> 

In der Tat gibt es keine "e" -Datei. Stein in Richtung Sberbank # 1. Wir schreiben einen Brief an die Sberbank (wir haben später eine Antwort erhalten, dass dies so sein sollte), und da keine Zeit für Korrespondenz vorhanden ist und wir sofort beginnen müssen, suchen wir nach Problemumgehungen, um das Ergebnis zu erhalten.

 04.02 12:28:55 SBKRNL: Failed to open device \\.\COM1, err 2 04.02 12:28:56 SBKRNL: Failed to open device \\.\COM1, err 2 04.02 12:28:56 SBKRNL: Result = 0 04.02 12:28:56 GATE: unlock:'00000054' 04.02 12:28:56 GATE: lock:'00000054' 'UPOSWINMUTEX2' 04.02 12:28:56 GATE: unlock:'00000054' 04.02 12:28:56 LOADPARM: Unloading GATE.DLL... 04.02 12:28:56 GATE: SB_KERNEL.DLL is unloaded 04.02 12:28:56 LOADPARM: GATE.DLL unloaded 

Ja, das Ergebnis kann aus dem Protokoll sbkernel.log abgerufen werden. Unbequem, außerdem gibt es keinen Karten-Hash, um anschließend "Danke" von Sberbank zu schrauben. Nicht gut.

Sie müssen eine Verbindung zur Bibliothek pilot_nt.dll herstellen und Funktionen daraus importieren. Alles wäre in Ordnung, aber ... Ein Stein in Richtung Sberbank Nr. 2: Unter Linux gibt es keine solche Bibliothek. Sie müssen zwei verschiedene Anwendungen für verschiedene Plattformen erstellen. Rufen Sie für Linux das Dienstprogramm sb_pilot auf (ähnlich wie loadparm.exe, übrigens Stein Nr. 3 für einen anderen Dienstprogrammnamen unter verschiedenen Plattformen ), unter Windows mit der Bibliothek pilot_nt.dll verbinden.

KAPITEL 5. Rätsel im Dunkeln


Um 19:00 Uhr.

Die Sberbank ist ein großes Unternehmen. Die meisten Softwarelösungen werden gemäß GOSTs und formalen Dokumenten hergestellt. Wir kommen in den Katalog, den die Sberbank mit den Bibliotheken liefert:

 Sberbank$ ls -l Docs  30160 drwx------ 2 alex alex 4096  17 19:31 FAQ -rw-rw-r-- 1 alex alex 3398465  9 2018   UPOS    ().docx -rw-rw-r-- 1 alex alex 1182078  9 2018   UPOS  .docx -rw-rw-r-- 1 alex alex 853504  9 2018   .doc drwx------ 3 alex alex 4096  31 17:11     -rw-rw-r-- 1 alex alex 5280787  9 2018    POS-.docx -rw-rw-r-- 1 alex alex 1149640  9 2018  .docx drwx------ 2 alex alex 4096  28 2018  UPOS drwx------ 2 alex alex 4096  28 2018    -rw-rw-r-- 1 alex alex 3451601  9 2018     ().docx -rw-rw-r-- 1 alex alex 1956196  9 2018   .docx -rw-rw-r-- 1 alex alex 1043161  9 2018       ()_().docx -rw-rw-r-- 1 alex alex 4348157  9 2018  POS-.docx -rw-rw-r-- 1 alex alex 3970267  9 2018   .docx drwx------ 3 alex alex 4096  28 2018   -rw-rw-r-- 1 alex alex 2644702  9 2018    POS-.docx drwx------ 2 alex alex 4096  28 2018   -rw-rw-r-- 1 alex alex 1558211  9 2018   .png 

Viel Gutes, aber wir interessieren uns nur für das Verzeichnis für Entwickler:

 Sberbank$ ls -l Docs/\ \ \ /  8704 -rw-rw-r-- 1 alex alex 47105  9 2018 1C.docx -rw-rw-r-- 1 alex alex 1824  9 2018 cardtype.h -rw-rw-r-- 1 alex alex 2590378  9 2018 cr_ttk_protocol_ru.rtf -rw-rw-r-- 1 alex alex 208  9 2018 deprtmnt.h -rw-rw-r-- 1 alex alex 16681  9 2018 errors.h drwx------ 6 alex alex 4096  28 2018 examples -rw-rw-r-- 1 alex alex 58575  9 2018 gate.h -rw-rw-r-- 1 alex alex 4218  9 2018 paramsln.h -rw-rw-r-- 1 alex alex 61693  9 2018 pilot_nt.h -rw-rw-r-- 1 alex alex 28160  9 2018 ReadTrack2.doc -rw-rw-r-- 1 alex alex 7417  9 2018 sbkernel.h -rw-rw-r-- 1 alex alex 144896  9 2018 sb_pilot.doc -rw-rw-r-- 1 alex alex 3525323  9 2018     ole- sbrf.dll.rtf -rw-rw-r-- 1 alex alex 46683  9 2018      gate.dll.chi -rw-rw-r-- 1 alex alex 255414  9 2018      gate.dll.chm -rw-rw-r-- 1 alex alex 814653  9 2018      gate.dll.pdf -rw-rw-r-- 1 alex alex 41618  9 2018      pilot_nt.chi -rw-rw-r-- 1 alex alex 241716  9 2018      pilot_nt.chm -rw-rw-r-- 1 alex alex 968753  9 2018      pilot_nt.pdf -rw-rw-r-- 1 alex alex 81  9 2018  .txt 

Viel Altpapier, nur für den Fall, lesen Sie pilot_nt erneut, aus dem wir Folgendes lernen:
Tabelle 1. Unterstütztes Betriebssystem sb_pilot.
BetriebssystemKapazitätModulname
Windows32sb_pilot.exe
Linux32sb_pilot
Dos16sb_pilot.exe

Es stellt sich heraus, dass das Dienstprogramm für Windows weiterhin sb_pilot heißen sollte. Nun, ein Stein in Richtung Sberbank Nr. 4 für die Inkonsistenz der eigenen Dokumentation.
Übertragung der Programmergebnisse.

Am Ende des Programms werden zwei Textdateien gebildet - die Austauschdatei und die Prüfdatei.

Die erste heißt e und soll Parameter der perfekten Operation an das aufrufende Programm übergeben. Die erste Zeile in dieser Datei enthält den Code des Ergebnisses der Operation und einen durch Kommas getrennten Text, der die Textnachricht erläutert. Code 0 bedeutet erfolgreiche Zahlung, jeder andere Wert - Verweigerung oder Zahlungsunfähigkeit.




Faul werfen wir noch einen Stein und beginnen, die Dokumentation für den direkten Anschluss der Bibliothek zu studieren.
Die Prozedur zum Aufrufen von Bibliotheksfunktionen

Wenn Sie Einkäufe mit Kreditkarte bezahlen (zurückgeben), muss das Bargeldprogramm die Funktion card_authorize () aus der Sberbank-Bibliothek aufrufen, indem Sie die Felder TType und Amount ausfüllen und in den verbleibenden Feldern Nullwerte angeben. Am Ende der Funktion müssen Sie das RCode-Feld analysieren. Wenn es den Wert "0" oder "00" enthält, gilt die Autorisierung als erfolgreich abgeschlossen, andernfalls wird sie abgelehnt. Außerdem müssen Sie den Wert des Felds Prüfen überprüfen.

Wenn es nicht NULL ist, muss es zum Drucken gesendet werden (im nicht steuerlichen Modus) und dann
Löschen Sie durch Aufrufen der Funktion GlobalFree (). Beim Schließen einer Schicht muss das Cash-Programm die Funktion close_day () aus der Sberbank-Bibliothek aufrufen, indem es das Feld TType = 7 ausfüllt und in den verbleibenden Feldern Nullwerte angibt. Überprüfen Sie am Ende der Funktion den Wert des Felds Prüfen.

Wenn das Feld Prüfen nicht NULL ist, muss es zum Drucken gesendet werden (im nicht steuerlichen Modus) und dann durch Aufrufen der Funktion GlobaFree () gelöscht werden.
Es klingt einfach, sogar eine Header-Datei wird bereitgestellt. Nun, stecken Sie es ein, kompilieren Sie und ...

 $ cat main.c && i686-w64-mingw32-gcc main.c -o main.a #include "pilot_nt.h" int main(void) { return 0; } In file included from main.c:1:0: pilot_nt.h:525:3: error: unknown type name 'auth_answer' auth_answer ans; /**< [in, out]                            .   . ::auth_answer */ ^ pilot_nt.h:544:3: error: unknown type name 'auth_answer' auth_answer ans; /**< [in, out]                            .   . ::auth_answer */ ^ pilot_nt.h:567:3: error: unknown type name 'auth_answer' auth_answer ans; /**< [in, out]                            .   . ::auth_answer */ ^ pilot_nt.h:590:3: error: unknown type name 'auth_answer' auth_answer ans; /**< [in, out]                            .   . ::auth_answer */ ^ pilot_nt.h:627:3: error: unknown type name 'auth_answer' auth_answer ans; /**< [in, out]                            .   . ::auth_answer */ ^ pilot_nt.h:668:3: error: unknown type name 'auth_answer' auth_answer ans; /**< [in, out]                            .   . ::auth_answer */ 

Ähm ... was? Öffnen Sie pilot_nt.h:

 #ifdef __cplusplus extern "C"{ #endif <...> /** *    * ,         . */ struct auth_answer{ int TType; /**< [in]  .  ::OpetationTypes */ unsigned long Amount; /**< [in]    */ char RCode[3]; /**< [out]    */ char AMessage[16]; /**< [out]    */ int CType; /**< [in,out]   */ char* Check; /**< [out]  ,   GlobalFree    */ }; <...> struct auth_answer7{ auth_answer auth_answ; /**< [in, out]   . . ::auth_answer */ <---- THIS char AuthCode[MAX_AUTHCODE]; /**< [out]  . 7 . */ char CardID [CARD_ID_LEN]; /**< [out]  . 25 . */ int SberOwnCard; /**< [out]     */ }; 

Sofort, ohne auf den Stein zu schauen, um Kommentare in russischer Sprache in CP1251-Codierung zu erhalten.

Nun, der ernsteste Stein: liebe C ++ - Entwickler. Wenn Sie extern "C" schreiben, bedeutet dies, dass der Code innerhalb des Blocks vom C-Compiler kompiliert werden muss. Wenn Sie aus einer Struktur KEIN typedef gemacht haben, müssen Sie jedes Mal, wenn Sie es als Typreferenz erwähnen, das Schlüsselwort` struct` schreiben.

Patchen Sie die Datei für Entwickler und ersetzen Sie das Wort "struct", wo immer dies erforderlich ist. Verknüpfung mit der Bibliothek `pilot_nt.dll`. Sieg, nein? Wir starten unsere Anwendung.

KAPITEL 6. Vom Feuer zu den Flammen


Nun, du verstehst es, richtig? Die Anwendung stürzt einfach ab. Bis zum Main. Meditieren Sie, fügen Sie das NIH-Analogon der Errno-Funktion für Windows hinzu: GetLastError (Stein Nr. 3 in Richtung Microsoft, die ersten beiden sind für Codierungen).

 C:\banks\sber\WIN>sb_pilot.exe 1 1000 E: !g_sblibrary (0xc0000096) 

0xc0000096? Sollte GetLastError nicht einen angemessenen Fehlercode zurückgeben?
Eine vollständige Liste der vom Betriebssystem bereitgestellten Fehlercodes finden Sie unter Systemfehlercodes.
Ja, öffnen Sie den Artikel hier :
Die folgenden Themen enthalten Listen mit Systemfehlercodes. Diese Werte werden in der Header-Datei WinError.h definiert.

  • Systemfehlercodes (0-499) (0x0-0x1f3)
  • Systemfehlercodes (500-999) (0x1f4-0x3e7)
  • Systemfehlercodes (1000-1299) (0x3e8-0x513)
  • Systemfehlercodes (1300-1699) (0x514-0x6a3)
  • Systemfehlercodes (1700-3999) (0x6a4-0xf9f)
  • Systemfehlercodes (4000-5999) (0xfa0-0x176f)
  • Systemfehlercodes (6000-8199) (0x1770-0x2007)
  • Systemfehlercodes (8200-8999) (0x2008-0x2327)
  • Systemfehlercodes (9000-11999) (0x2328-0x2edf)
  • Systemfehlercodes (12000-15999) (0x2ee0-0x3e7f)
Ok, wir haben einen undokumentierten Fehler, wir werfen einen Stein und öffnen das allwissende Google:


Das Wesentliche des Fehlers ist, dass eine Unterroutine eine der Anweisungen verwendet

  • _inp ()
  • _inpw ()
  • _inpd ()
  • _outp ()
  • _outpw ()
  • _outpd ()

Die Verwendung ist unter NT-Kernen verboten, da diese versuchen, direkt mit dem parallelen Port zu arbeiten. Anscheinend wird dieser Code im Bibliotheksinitialisierer aufgerufen, d.h. Die Bibliothek möchte beim Start die Ports nach Geräten abfragen, der NT-Kernel erfordert jedoch Arbeit über den Treiber.

Hoffnungslose Situation?

KAPITEL 8. Spinnen und Fliegen


22 Uhr Nur für den Fall, die Idee entsteht zu überprüfen, dass dies nicht auf die Tatsache zurückzuführen ist, dass wir Cross-Compiling mit Linux unter Verwendung von mingw verwenden. Gleichzeitig wissen wir, dass Sberbank nur eine 32-Bit-Anwendung bereitstellt, sodass es mit einer 64-Bit-Anwendung nicht funktioniert. Okay, aber wir werden 2019 einen Stein in Richtung Sberbank für die Nur-32-Version herausbringen.

Gegeben : installiert in Virtualbox Windows 7;
Erforderlich : Installieren Sie Visual Studio und kopieren Sie MVP.

Wir gehen auf die Microsoft-Website und laden Visual Studio 2017 herunter. Wir nehmen die Community-Lizenz, da wir sie zur Überprüfung verwenden. Für Unternehmen wird die Lizenz gekauft, wenn sie abhebt.
Laden Sie ein paar hundert Megabyte herunter und ...

Wir sehen, dass unsere Version des Betriebssystems (Windows 7) nicht unterstützt wird.

Ok, wir gehen zu allen Arten von obszönen Sites, suchen Visual Studio 2008, laden wieder ein paar hundert Megabyte herunter und ...

Wir bekommen die ISO-Datei.

Okay, versuchen wir, Daemon Tools 10 zu installieren (da dies die von der Site angebotene Version ist), um diese virtuelle Festplatte einzulegen.

Führen Sie das heruntergeladene Binar aus. Fehlzündung, erfordert .NET Framework 4.5, herunterladen, installieren.
Wir starten das heruntergeladene Binar, die Installation hat begonnen, der Bootloader sagt, dass es 4.5.2 benötigt, herunterladen, installieren.
Wir starten das heruntergeladene Binar, die Installation hat begonnen, der Bootloader sagt, dass es nirgendwo hingehen wird, bis wir das Sicherheitsupdate KB3033929 installieren, herunterladen, installieren.

Und wir bekommen einen Schlag ins Gesicht von Microsoft als Nachricht:



Wir werfen gewaltsam einen sehr scharfen Stein in Richtung Microsoft, laden die alten Daemon Tools von Torrents herunter, entpacken Visual Studio erfolgreich, installieren, kompilieren schließlich (00:00) MVP, wir erhalten den gleichen Fehler. Nun, es gab eine gute Version, aber sie ist nicht zusammengewachsen.

KAPITEL 11. Auf der Schwelle


Wir schreiben an den zweiten Programmierer, der in diesem Moment den Server und den Registrierungsvorgang dringend abschließt. Er erinnert sich, dass es ein Git- Repository gibt , das unter NT eine Verbindung zu dieser Bibliothek herstellt und damit arbeitet.

Schauen Sie sich das Repository misstrauisch an, laden Sie es herunter, kompilieren Sie es und führen Sie es aus. Es funktioniert.



Wir sehen den Code noch misstrauischer an. Der Code ist identisch, außer dass er in C ++ und nicht in C geschrieben ist.
Wir verstehen, dass Sprache nichts damit zu tun hat. Wir schauen uns die Bibliotheken der Sberbank an, die der Code zieht.
Wir sehen das letzte Commit.

Und hier warten wir auf eine weitere Überraschung.

Es stellt sich heraus, dass die Versionen der Sberbank-Bibliothek unterschiedlich sein können. Das letzte Commit erhöht die Version von 23 auf 27. Kopieren Sie die Version von der Gita auf Ihren Testcomputer - WORKS!

Wir überprüfen alle von Sberbank gesendeten Archive, vergleichen die Versionen und bauen ein Tablet:
VersionFunktioniert
26.0.15 - GrundlegendNein
27.4.12 - Aus dem Repositoryja
23.0.13 - Aus dem Repositoryja
29.0.9 - Spätestens ab Sa.ja
23.0.13 - Mit einem Patch für das Cryptor-Systemja

Großartig, jetzt lass uns heilen. Auf den Systemen, auf denen es 26 kostet, aktualisieren Sie auf 29 oder 27 und alles wird abheben.
Wir werfen Stein Nr. 9 in Richtung Sberbank, um das Verhalten auf NT-Systemen zu brechen.

KAPITEL 12. Was sie drinnen erwartete


Nicht genug E-Datei? Es spielt keine Rolle, wir nehmen gepatchte Überschriften, verknüpfen sie dynamisch mit der Bibliothek, um einen Fehler korrekt zurückzugeben, schreiben einen Code, der einfach den Rückkehrcode von der Funktion in die "e" -Datei schreibt, rufen wir den biner sb_pilot.exe auf und ...

Um zu arbeiten, funktioniert es.

Das ist nur die Version für das "Cryptor" -System erstellt keine "p" -Datei.

Wir schauen traurig auf das Blut, das über die Knöchel tropft, und auf die Delle in der Wand.

Was ist für den Anfang das Cryptor-System?

Cryptera ist ein dänisches Unternehmen, das Verschlüsselungsgeräte / Sicherheitsgeräte / Schlüssel usw. herstellt. Ich denke, Sie alle haben eine der Kopien ihrer Produkte gesehen:



Daher verwendet Sberbank ihr Kryptomodul für Pinpads und veröffentlicht eine spezielle "gepatchte" Bibliothek, in der, wie wir bereits verstanden haben, die "p" -Datei nicht erstellt wird. Wir schreiben darüber an Sberbank und werden in wenigen Tagen die Antwort erhalten, dass "unter dem ursprünglichen System die Datei" p "erstellt wird, unter dem auf Cryptor gepatchten jedoch nicht." Wir werden ihnen in ein paar Tagen Stein Nr. 10 geben, weil Sie jetzt arbeiten müssen.

Glücklicherweise oder unglücklicherweise geben die Funktionen, mit denen wir Operationen ausführen, die bereits erwähnte Struktur zurück:

 struct auth_answer{ int TType; /**< [in]  .  ::OpetationTypes */ unsigned long Amount; /**< [in]    */ char RCode[3]; /**< [out]    */ char AMessage[16]; /**< [out]    */ int CType; /**< [in,out]   */ char* Check; /**< [out]  ,   GlobalFree    */ }; 

Oh, gut, der Scheck ist bereits da, wir können ihn selbst in einer Datei speichern oder sofort in JSON drucken ...

 printf("%s\n", answer.Check); 

Und die Anwendung stürzt ab, weil auf einen ungültigen Zeiger zugegriffen wird.

KAPITEL 14. Feuer und Wasser


4 Uhr morgens Wir führen Seth Bandha Sarvangasana durch, um uns zu beruhigen, und lesen das Handbuch sorgfältig durch:
Das [out] -Bild der Prüfung sollte von GlobalFree im aufrufenden Programm freigegeben werden
Was gibt uns das? Viel. Erstens, da der Zeiger mit GlobalFree bereinigt werden muss, wurde er mit GlobalAlloc gespeichert . Daher wird kein Zeiger auf den Speicher ausgegeben, wie dies in der 16-Bit-Version der Fall war, sondern eine Objektnummer mit dem semantisch deklarierten Typ HGLOBAL, die in der GlobalSize-Funktion eingegeben werden kann, um die Größe des zugewiesenen Blocks zu ermitteln, und GlobalLock, um einen Speicherabschnitt zu blockieren, aber den ursprünglichen Zeiger abzurufen. Übrigens Stein Nr. 6 in Richtung Microsoft für NIH malloc und kostenlos, die in der Standardbibliothek sind.

 printf("%s\n", GlobalLock(answer.Check)); 

Und trotzdem einen Tropfen bekommen. Okay, was zeigt GlobalSize? Null? Irgendwie seltsam.

Wir überprüfen andere Funktionen, die ebenfalls einen Fehler ergeben sollten - wir sehen das gleiche Bild.

Mir fällt nur ein, dass ich selbst einen Beleg gemäß den Daten generiere, die die coolste Zahlungsfunktion liefern kann (ja, Sberbank hat Funktionen namens card_authorize2..14, ich werde keinen Stein dafür werfen):

 struct auth_answer14 { auth_answer ans; /**< [in, out]   . . ::auth_answer */ char AuthCode[MAX_AUTHCODE]; /**< [out]  . 7 . */ char CardID[CARD_ID_LEN]; /**< [out]  . 25 .     ,   6   4,    '*'.*/ int ErrorCode; /**< [out]  . */ char TransDate[TRANSDATE_LEN]; /**< [out]     */ int TransNumber; /**< [out]    . , .    */ int SberOwnCard; /**< [out]     */ char Hash[CARD_HASH_LEN]; /**< [in, out]  SHA1   ,   ASCII     . 40 .*/ char Track3[CARD_TRACK3_LEN]; /**< [out]   */ DWORD RequestID; /**< [in,out]   .  PCI DSS .*/ DWORD Department; /**< [in]     0  14-, .      0xFFFFFFFF,          .        ,      4191. */ char RRN[MAX_REFNUM]; /**< [in,out]   ,  .    ,     .   12-  .       (   pilot_nt.dll),     –  (     ;     ,   ).*/ DWORD CurrencyCode; /**< [in]    (810, 643, 840, 978  ..) */ char CardEntryMode; /**< [out]    ('D'-., 'M'- , 'C'-, 'E'- EMV, 'R'- magstripe, 'F'-fallback)*/ char CardName[MAX_CARD_NAME_LEN]; /**< [out]    */ char AID[MAX_AID_ASCII_LEN]; /**< [out] Application ID   (   ASCIIZ-)*/ char FullErrorText[MAX_FULL_ERROR_TEXT]; /**< [out]     */ DWORD GoodsPrice; /**< [in]    ,  (34.99->3499)*/ DWORD GoodsVolume; /**< [in]  ,  .  (30.234->30234)*/ char GoodsCode[MAX_GOODS_CODE+1]; /**< [in]     .*/ char GoodsName[MAX_GOODS_NAME]; /**< [in]     . !   auth_answer14         gate.dll TGoodsData.     */ }; /** @brief     * @param[in] track2      .  NULL,     . * @param[in,out] auth_answer . ::auth_answer14 * @param[in,out] payinfo     * @return int  . */ PILOT_NT_API int card_authorize14( char *track2, struct auth_answer14 *auth_answer, struct payment_info_item *payinfo ); 

Wir versuchen, die Felder auszuwählen ... Wir stellen fest, dass nur eines uns vom Glück trennte - Nachname und Name des Karteninhabers. Ohne sie gilt ein Beleg nicht als legal :
Details: Kennung eines Geldautomaten, eines elektronischen Terminals oder anderer technischer Mittel, die für Transaktionen mit Zahlungskarten bestimmt sind; Art der Operation; Datum der Transaktion; Transaktionsbetrag; Transaktionswährung; Autorisierungscode für Provisionsbetrag; Zahlungskartendetails.
Es ist schade, aber mit den Daten, die wir haben, einen rechtlichen Beleg zu erstellen, wird nicht funktionieren.

Lassen Sie uns noch einmal in die Dokumentation eintauchen.

Das Beispiel, das die Sberbank liefert, finden wir im Verzeichnis „Beispiele“

 std::cout << "Authorization completion finished with code '" << result << "'" << std::endl; std::ofstream file(CHEQUE_FILENAME); file << argument.auth_answ.Check; file.close(); if (argument.auth_answ.Check) { std::cout << "Cheque saved to file " << CHEQUE_FILENAME << std::endl; //GlobaFree(argument.auth_answ.Check); } 

Es zeigt einfach den Text auf dem Zeiger an. Aber wir haben bereits gesehen, dass es so nicht funktioniert ... Nur für den Fall, wir werden ihr Beispiel kompilieren und es ausführen. Abfahrt in der Zeile `file << argument.auth_answ.Check;`, na ja, Sberbank, halten Sie Stein Nr. 11 für nicht funktionierende Beispiele.

7 Uhr morgens Sie können bereits an die Entwickler eines anderen Wrappers schreiben, der vor einigen Jahren in Delphi geschrieben wurde. Wir bekommen die Antwort, dass alles für sie funktioniert. Wir suchen nach der Basis ihres Wrappers und finden auf Github :

 TAuthAnswer = packed record TType: integer; Amount: UINT; // IN     Rcode: array [0 .. 2] of AnsiChar; AMessage: array [0 .. 15] of AnsiChar; CType: integer; Check: PAnsiChar; end; Result := Func(nil, @FAuthAnswer); FLastError := Result; FCheque := PAnsiChar(FAuthAnswer.Check); 

Einfache Konvertierung von Typ zu Zeiger ohne Funktionsaufrufe.

Wir beginnen böse Geister zu verdächtigen.

KAPITEL 17. Gewitter brach aus


Die Leute kehren ins Büro zurück und nicken mitfühlend. PO freut sich nicht sehr über die neuesten Nachrichten.

Hier erinnere ich mich an ein Detail. Als wir die Felder der Struktur Nr. 14 anzeigten, um ihre Werte anzuzeigen, wurde ein Byte jeder Zeile abgeschnitten. Einerseits, andererseits
Achtung!In der auth_answer14-Struktur ist der Produktname ein Zeichen kürzer als in gate.dll TGoodsData. Beheben Sie diesen Fehler standardmäßig
Vielleicht hängt es immer noch mit ... Eine

schreckliche Vermutung dämmert dem Gehirn wie ein Blitz. Deklarieren Sie die Struktur als

 typedef struct __attribute__((packed)) { int TType; /**< [in]  .  ::OpetationTypes */ unsigned long Amount; /**< [in]    */ char RCode[3]; /**< [out]    */ char AMessage[16]; /**< [out]    */ int CType; /**< [in,out]   */ char* Check; /**< [out]  ,   GlobalFree    */ }; 

Und ...

nichts ändert sich.

Standbildgröße = 0, Standbildsperre = NULL.

Schmerz

Verfall.

Unwillkürlich suchen Sie mit Ihren Augen nach einem bequemen Balken an der Decke, der dem Gewicht standhält. Nach so vielen Stunden ohne Unterbrechung des Codierens und Studierens der Dokumentation schweben schlanke Byte-Reihen vor unseren Augen. Was aber, wenn wir Bytes drucken, die im Allgemeinen zurückgegeben werden?

  u32 i; for (i = 0; i < sizeof(answ); i++) { printf("%02x ", *((u8 *)&answ + i)); } printf("\n"); C:\banks\sber\sb_pilot>sb_pilot.exe 1 1000 01 00 00 00 e8 03 00 00 30 00 00 ce e4 ee e1 f0 e5 ed ee 00 00 00 00 00 00 00 00 02 00 00 00 f8 6c 7a 00 00 

`30 00 00 ce` - was bedeutet, dass die Sberbank weiterhin gepackte Strukturen verwendet. Aber dazu gibt es in den Überschriften kein Wort. Daher funktionieren die Beispiele nicht, daher ist es unmöglich, am Ende einen Zeiger auf den Text zu erhalten, da dieser aufgrund einer Verschiebung von 1 Byte fehlerhaft ist. Riesiger und stacheliger Stein in Richtung Sberbank!

Und dann fiel mir eine maaaalenky-Nuance auf. 4 + 4 + 3 + 16 + 4 + 4 = 35. Und hier sind 36 Bytes, Obelix.

Wenn 36 Bytes vorhanden sind, richtet der Compiler die Struktur weiterhin aus. Zwischen RCode und AMessage wird also noch ein zusätzliches Byte eingefügt. Aber warum? Immerhin haben wir `__packed__` angegeben!

KAPITEL 18. Die Rückreise


Die Gründe, warum die Ausrichtung noch aktiv ist, wurden 2012 veröffentlicht: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 . Ein Fehler wurde nur in GCC 8 behoben (ein Stein für 6 Jahre Buggy!), Der noch nicht aktualisiert werden kann. Zum Glück gibt es eine Problemumgehung:

 -mno-ms-bitfields 

Wir werden jetzt den Funktionsmechanismus dieses Flags nicht analysieren, sondern nur an den Compiler übergeben:



Slip! Liebling! Ich habe dich vermisst, ich werde nicht einmal wegen des Krakozyabry schwören, ich habe bereits einen Stein dafür geworfen.

Und schließlich geben wir Microsoft einen Stein, weil GlobalSize / Lock ungültigen Zeigern Nullen gibt.

KAPITEL 19. Letztes Kapitel


Um die Anzahl der ifdefs für die Ebene für sb_pilot zu minimieren, haben wir eine separate Anwendung geschrieben, die die Linux-Version von sb_pilot vollständig imitiert. Wenn Sie also den Layer-Code Nr. 1 gleich lassen, bleibt nur eine Bedingung:

 #if defined(BXI_OS_GLX) #define GFJ_PILOT_EXECUTABLE "./sb_pilot" #elif defined(BXI_OS_WIN) #define GFJ_PILOT_EXECUTABLE "./sb_pilot.exe" #endif 



Ergebnisse der Schlacht:

  • Sberbank: 12 Steine
  • Microsoft: 7 Steine
  • GCC: 1 Stein

Leistungserinnerung auf unserer Kommandotafel:

Source: https://habr.com/ru/post/de451188/


All Articles