[From the sandbox] Sberbank or back and forth

[From the sandbox] Sberbank or back and forth




CHAPTER 1. Unexpected guests


It all started on that ill-fated morning, when the Project Manager announced that the project timeline should be quickly and decisively reduced by a month. More precisely, the project should be ready in 4 days. No, our PO is not a beast, and is not at all like an owl (except maybe a little bit for a crow), it just happened . Well, if it is necessary, it is necessary, especially since the team (and I am the leading developer of the “C” team) was promised something tasty. The clock and calendar was Thursday, 11:00, by Monday the project should be ready.

To begin with, what we do. We are engaged in cinema automation - automatic and remote control of equipment, cinema automation, monitoring, video panels, and now also ticket and bar sales terminals. Specifically, the last paragraph and is devoted to this article.

The project itself, which had to be completed before Monday, is a kind of layer between the main server on Scala and the iron payment terminal VeriFone VX 820 (in fact, there are more terminals, but for example we take only it). It is clear that no one will give us transactions just like that through him, therefore utilities and libraries of Sberbank/Arcus and UCS are used. Thus the scheme of work in the end should be as follows:



Outwardly, it looks like this:



Also, this subsystem should be used on standard cash register machines, which everyone saw in any cinema at the cashiers.

According to internal tradition, we call each project of our team a name from the Norse mythology, the name Gefjon was chosen for this subsystem - the name of the goddess of fertility and abundance (a good name for a payment server, isn't it? Well, the legend about bulls cutting off the island ideally falls on the current architecture, cutting off work with equipment from a high-level language).

The format of incoming and outgoing messages is an HTTP server with JSON load. This is the best compromise between Scala, which is difficult to descend to isolating binary data from socket-threads and C, which is difficult to rise to transfer objects through the network. There are not so many possible operations that need to be operated on: payment, cancellation, refund, various types of reports, opening the service menu and ping. It looks nothing complicated. Since there are three banking systems (and the completion of the family is expected later), it was decided to divide the project into components:



The blocks that we needed to make were painted green, the ones that could not be changed and that the bank provided were blue.

Since the main problems arose only with the software from Sberbank, the article as a whole will be devoted to the pitfalls that we recounted with our rook.

CHAPTER 2. Lamb Roast



(photo: heaclub.ru)

... looks like this. The code of the prototype, which was written a few months ago to make it clear to all superiors, that we can work with banking applications, looked like this.

  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 & gt;% s 2 & gt;% s ||"
  "echo%" PRIi32 "= 9.6, PINPAD_TEST & gt; & gt;% 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  

It is clear that for the Production option it was no good, so it was necessary in essence to write it all over again.

Each bank that provides libraries for working with the terminal usually provides two connection options: through the library functions (.so/.dll) or through a ready-made utility, which only needs to pass two values ​​— the type of operation and the amount (when needed). In theory, nothing complicated, just something

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

The result of the operation will be placed in the file “e”, and the slip-check - in the file “p”. Just send these files to stdout with conversion to JSON, so that the HTTP server just sends them up as payload without thinking about what is there.

But this article would not have been published if everything were so simple.

CHAPTER 4. Through the mountain and under the mountain


The initial implementation was a simple application call — the HTTP server called the necessary wrapper with unified parameters (for example, the X report was 4), and the utility, for example, gfj_pilot, ran sb_pilot with the parameter required for this operation (for example, the X report was 9) . Then the utility wrapper read from the e-file the result of the operation (for example, 2000 - “payment failure, repeat the operation”) and converted it into a universal error (for example, 3 - “Error reading or processing the card/account, repeat the operation”). After that, the “p” file was converted to base64 to avoid formatting and sent along with the result to stdout as JSON.

All this worked fine, until one day we were informed that ...

... it doesn't work under Windows.



Well, more precisely, Windows itself has no problems (except that the slip is generated in Cp-1251 encoding, and the console works in CP866). Just did not generate the "e" file. We launched the banking utility directly:

  C: \ banks \ sber \ sb_pilot & gt; dir
  Volume in device C has no label.
  Volume Serial Number: B401-6B9D
 
  The contents of the folder C: \ banks \ sber \ sb_pilot
 
 02/04/2019 12:28 & lt; DIR & gt;  .
 02/04/2019 12:28 & lt; DIR & gt;  ..
 01/31/2019 5:12 PM 10,832 F12X24.BIN
 01/31/2019 17:12 128,000 gate.dll
 01/31/2019 17:12 72 192 loadparm.exe
 01/31/2019 5:12 PM 36 204 OPT0.R
 01/31/2019 5:12 PM 20 716 OPT1.R
 01/31/2019 5:12 PM 1 806 OPT3.R
 01/31/2019 17:12 388 608 pilot_nt.dll
 01/31/2019 11:06 PM 463 pinpad.ini
 01/31/2019 17:12 91 136 posScheduler.exe
 01/31/2019 5:12 PM 418 printers.ini
 02/01/2019 16:51 91 646 sbkernel1902.log
 01/31/2019 5:12 PM 653 312 sbrf.dll
 01/31/2019 5:12 PM 840 192 SBRFCOM.dll
 01/31/2019 17:12 3,142,656 sb_kernel.dll
 02/01/2019 16:51 9 SESS.D
 02/01/2019 16:51 715 SPLC.D
 01/31/2019 17:12 72 192 upwin.exe
  20 files 5,659,718 bytes
  2 folders 37,567,004,672 bytes free
 
 # We send the payment team (1) for 10 rubles (1000 kopecks)
 C: \ banks \ sber \ sb_pilot & gt; loadparm.exe 1 1000
 
 C: \ banks \ sber \ sb_pilot & gt; dir
  Volume in device C has no label.
  Volume Serial Number: B401-6B9D
 
  The contents of the folder C: \ banks \ sber \ sb_pilot
 
 02/04/2019 12:28 & lt; DIR & gt;  .
 02/04/2019 12:28 & lt; DIR & gt;  ..
 02/04/2019 12:28 PM 216 commerr.log
 01/31/2019 5:12 PM 10,832 F12X24.BIN
 01/31/2019 17:12 128,000 gate.dll
 01/31/2019 17:12 72 192 loadparm.exe
 01/31/2019 5:12 PM 36 204 OPT0.R
 01/31/2019 5:12 PM 20 716 OPT1.R
 01/31/2019 5:12 PM 1 806 OPT3.R
 02/01/2019 18:51 1 349 p
 01/31/2019 17:12 388 608 pilot_nt.dll
 01/31/2019 11:06 PM 463 pinpad.ini
 01/31/2019 17:12 91 136 posScheduler.exe
 01/31/2019 5:12 PM 418 printers.ini
 02/04/2019 12:28 92 218 sbkernel1902.log
 01/31/2019 5:12 PM 653 312 sbrf.dll
 01/31/2019 5:12 PM 840 192 SBRFCOM.dll
 01/31/2019 17:12 3,142,656 sb_kernel.dll
 02/01/2019 16:51 9 SESS.D
 01.02.2019 16:51 715 SPLC.D
 01/31/2019 17:12 72 192 upwin.exe
  19 files 5,659,029 bytes
  2 folders 37,567,008,768 bytes free
 
 C: \ banks \ sber \ sb_pilot & gt;  

Indeed, there is no “e” file. Stone in the direction of Sberbank # 1. We are writing a letter to the savings bank (later we received the answer that this should be so), and since there is no time for correspondence and we need to start right now, we are looking for workarounds for obtaining the result.

  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  

Yeah, the result can be obtained from the log sbkernelGGMM.log. It’s inconvenient, plus there isn’t a card hash to subsequently tie up “Thank you” from the savings bank. Not suitable.

You will have to connect to the pilot_nt.dll library and import functions from it. Everything would be fine, but ... Stone towards Sberbank # 2: under Linux there is no such library, you will have to create two different applications for different platforms - for linux you need to call the utility sb_pilot (analog loadparm.exe, by the way stone # 3 for the different name of the utility under different platforms ), under windows connect to the pilot_nt.dll library.

CHAPTER 5. Mysteries in the dark


On the clock 19:00.

Sberbank is a large company, most software solutions are manufactured according to GOST and formal documents. We climb into the catalog that Sberbank supplies with libraries:

  Sberbank $ ls -l Docs
 total 30160
 drwx ------ 2 alex alex 4096 Jan 17 19:31 FAQ
 -rw-rw-r-- 1 alex alex 3398465 May 9 2018 Basic UPOS configuration for a standalone solution (AP) .docx
 -rw-rw-r-- 1 alex alex 1182078 May 9 2018 Basic UPOS setting for ikr.docx
 -rw-rw-r-- 1 alex alex 853504 May 9,201 Versions and changes.doc
 drwx ------ 3 alex alex 4096 Jan 31 17:11 For software developers KKM
 -rw-rw-r-- 1 alex alex 5280787 May 9 2018 Download software to POS-terminals.docx
 -rw-rw-r-- 1 alex alex 1149640 May 9 2018 Error codes.docx
 drwx ------ 2 alex alex 4096 May 28 2018 UPOS setting
 drwx ------ 2 alex alex 4096 May 28 2018 Setting up cash programs
 -rw-rw-r-- 1 alex alex 3451601 May 9 2018 Determining the autonomous solution scheme (AP) .docx
 -rw-rw-r-- 1 alex alex 1956196 May 9 2018 Definition of the scheme IKR.docx
 -rw-rw-r-- 1 alex alex 1043161 May 9 2018 Memo on setting up the PAYMENT of air tickets function (Aeroflot) _ (ICR) .docx
 -rw-rw-r-- 1 alex alex 4348157 May 9 2018 POS-terminal options.docx
 -rw-rw-r-- 1 alex alex 3970267 May 9 2018 Connecting individual functions.docx
 drwx ------ 3 alex alex 4096 May 28 2018 User Manuals
 -rw-rw-r-- 1 alex alex 2644702 May 9 2018 POS Terminal Configuration Guide.docx
 drwx ------ 2 alex alex 4096 May 28 2018 Accompanying documentation
 -rw-rw-r-- 1 alex alex 1558211 May 9 2018 Document content schema.png  

A lot of good, but we are only interested in the directory for developers:

  Sberbank $ ls -l Docs/For \ developers \ software \ CMC/
 total 8704
 -rw-rw-r-- 1 alex alex 47105 May 9 2018 1C.docx
 -rw-rw-r-- 1 alex alex 1824 May 9 2018 cardtype.h
 -rw-rw-r-- 1 alex alex 2590378 May 9 2018 cr_ttk_protocol_ru.rtf
 -rw-rw-r-- 1 alex alex 208 May 9 2018 deprtmnt.h
 -rw-rw-r-- 1 alex alex 16681 May 9 2018 errors.h
 drwx ------ 6 alex alex 4096 May 28 2018 examples
 -rw-rw-r-- 1 alex alex 58575 May 9 2018 gate.h
 -rw-rw-r-- 1 alex alex 4218 May 9 2018 paramsln.h
 -rw-rw-r-- 1 alex alex 61693 May 9 2018 pilot_nt.h
 -rw-rw-r-- 1 alex alex 28160 May 9 2018 ReadTrack2.doc
 -rw-rw-r-- 1 alex alex 7417 May 9 2018 sbkernel.h
 -rw-rw-r-- 1 alex alex 144896 May 9 2018 sb_pilot.doc
 -rw-rw-r-- 1 alex alex 3525323 May 9 2018 Integration with CMC via ole-object sbrf.dll.rtf
 -rw-rw-r-- 1 alex alex 46683 May 9 2018 Integration with CMC through the library gate.dll.chi
 -rw-rw-r-- 1 alex alex 255414 May 9 2018 Integration with CMC through the library gate.dll.chm
 -rw-rw-r-- 1 alex alex 814653 May 9 2018 Integration with CMC through the library gate.dll.pdf
 -rw-rw-r-- 1 alex alex 41618 May 9 2018 Integration with KKM through the pilot_nt.chi library
 -rw-rw-r-- 1 alex alex 241716 May 9 2018 Integration with CCM through the library pilot_nt.chm
 -rw-rw-r-- 1 alex alex 968753 May 9 2018 Integration with CMC through the pilot_nt.pdf library
 -rw-rw-r-- 1 alex alex 81 May 9 2018 Pinpad subtypes.txt  

A lot of waste paper, just in case, once again reread pilot_nt, from which we learn the following:
Table 1. Supported sb_pilot OS.
OS Digit Module Name
Windows 32 sb_pilot.exe
Linux 32 sb_pilot
DOS 16 sb_pilot.exe

It turns out the utility under windows should still be called sb_pilot. Well, a stone in the direction of Sberbank # 4 for the discrepancy of its own documentation.
Transfer the results of the program.

At the end of the program, two text files are created - an exchange file and a check file.

The first is named e and is intended to pass the parameters of the operation to the calling program. The first line in this file contains the result code of the operation, and a comma-separated explanatory text message. Code 0 means a successful payment, any other value - refusal or failure to make a payment.




Lazily we throw another stone and begin to study the documentation for connecting the library directly.
Order of library functions

When paying (returning) a purchase with a bank card, the cash program should call the function card_authorize () from the Sberbank library by filling in the TType and Amount fields and specifying zero values ​​in the other fields. At the end of the function, it is necessary to analyze the RCode field. If it contains the value "0" or "00", the authorization is considered successful, otherwise rejected. In addition, you need to check the value of the Check field.

If it is not NULL, you must print it (in non-fiscal mode) and then
delete by calling the GlobalFree () function. When closing the shift, the cash program should call the close_day () function from the Sberbank library by filling in the TType = 7 field and specifying zero values ​​in the remaining fields. Upon completion of the function, it is necessary to check the value of the Check field.

If the Check field is not NULL, you must send it to print (in non-fiscal mode) and then delete it by calling the GlobaFree () function.
It sounds easy, even the header file is provided. Well, we connect it, compile it and ...

  $ cat main.c & amp; & amp;  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;/** & lt;  [in, out]                            .    .:: auth_answer */
  ^
 pilot_nt.h: 544: 3: error: unknown type name ‘auth_answer’
  auth_answer ans;/** & lt;  [in, out]                            .    .:: auth_answer */
  ^
 pilot_nt.h: 567: 3: error: unknown type name ‘auth_answer’
  auth_answer ans;/** & lt;  [in, out]                            .    .:: auth_answer */
  ^
 pilot_nt.h: 590: 3: error: unknown type name ‘auth_answer’
  auth_answer ans;/** & lt;  [in, out]                            .    .:: auth_answer */
  ^
 pilot_nt.h: 627: 3: error: unknown type name ‘auth_answer’
  auth_answer ans;/** & lt;  [in, out]                            .    .:: auth_answer */
  ^
 pilot_nt.h: 668: 3: error: unknown type name ‘auth_answer’
  auth_answer ans;/** & lt;  [in, out]                            .    .:: auth_answer */ 

Ummm ... What? Open pilot_nt.h:

  # ifdef __cplusplus
 extern "C" {
 #endif
 & lt; ... & gt;/**
  * Basic parameters of the operation
  * The structure used to describe the operation and obtain the results of the operation.
  */
 struct auth_answer {
  int TType;/** & lt;  [in] transaction type.  see :: OpetationTypes */
  unsigned long Amount;/** & lt;  [in] amount in kopecks */
  char RCode [3];/** & lt;  [out] authorization result code */
  char AMessage [16];/** & lt;  [out] text authorization result */
  int CType;/** & lt;  [in, out] card type */
  char * Check;/** & lt;  [out] check image, GlobalFree should be released in the calling program */
 };
 & lt; ... & gt;
 struct auth_answer7 {
  auth_answer auth_answ;/** & lt;  [in, out] The main parameters of the operation.  See :: auth_answer */& lt; ---- THIS
  char AuthCode [MAX_AUTHCODE];/** & lt;  [out] Authorization code.  7 bytes  */
  char CardID [CARD_ID_LEN];/** & lt;  [out] Card ID.  25 bytes  */
  int SberOwnCard;/** & lt;  [out] Sberbank card ownership flag */
 };  

Immediately, without looking at the stone for comments in Russian encoded in CP1251.

Well, the most serious stone: dear C ++ developers. If you write extern "C" - this means that the code inside the block must be compiled by the C-compiler. If you have NOT made a `typedef` structure, then each time it is mentioned as a type indication, you must write the keyword` struct`.

Patches a file for developers, substituting wherever the word `struct` is needed. Link to the `pilot_nt.dll` library. Victory, no? Launch our app.

CHAPTER 6. From fire yes to the fire


Well, you understand, right? The app just crashes. Immediately, before the main. We meditate, add the NIH analogue of the errno function for windows: GetLastError (stone # 3 towards Microsoft, the first two for encodings).

  C: \ banks \ sber \ WIN & gt; sb_pilot.exe 1 1000
 E:! G_sblibrary (0xc0000096)  

0xc0000096? Shouldn't GetLastError return an adequate error code?
For a complete list of error codes provided by the operating system, see System Error Codes.
Yeah, open the article on link :
These values ​​are defined in the WinError.h header file.

  • System Error Codes (0-499) (0x0-0x1f3)
  • System Error Codes (500-999) (0x1f4-0x3e7)
  • System Error Codes (1000-1299) (0x3e8-0x513)
  • System Error Codes (1300-1699) (0x514-0x6a3)
  • System Error Codes (1700-3999) (0x6a4-0xf9f)
  • System Error Codes (4000-5999) (0xfa0-0x176f)
  • System Error Codes (6000-8199) (0x1770-0x2007)
  • System Error Codes (8200-8999) (0x2008-0x2327)
  • System Error Codes (9000-11999) (0x2328-0x2edf)
  • System Error Codes (12000-15999) (0x2ee0-0x3e7f)
Great, we got an undocumented error, throw a stone and open an all-knowing google:


The essence of the error is that some subroutine uses one of the instructions

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

The use of which is prohibited under NT-cores, as they are trying to work with the parallel port directly. Apparently, this code is called in the library initializer, i.e. the library at startup wants to poll the ports for the presence of devices, but the NT core requires work through the driver.

A hopeless situation?

CHAPTER 8. Spiders and flies


22:00 Just in case, the idea arises to check that this is not due to the fact that we are using cross-compilation with Linux using mingw. At the same time, we understand that Sberbank delivers only a 32-bit application, so linking with a 64-bit application will not work, okay, but we still run the stone in the direction of Sberbank for the 32-only version in the 2019th year.

Given : installed in virtualbox windows 7;
Required : install Visual Studio and build MVP.

We go to the Microsoft website, download Visual Studio 2017. We take a community license, as we take it for verification, for business a license will be purchased if it takes off.
Download a few hundred megabytes and ...

See that our OS version (Windows 7) is not supported.

Ok, we go to all sorts of obscene sites, we are looking for Visual Studio 2008, download a few hundred megabytes again and ...

Get the iso file.

Okay, let's try to install Daemon Tools 10 (as this is the version that the site offers) to insert this virtual disk.

Run the downloaded binary. Misfire, requires .NET Framework 4.5, download, set.
We launch the downloaded binary, the installation has begun, the bootloader says that it needs 4.5.2, we download, we install.
We launch the downloaded binary, the installation has begun, the bootloader says that it will not go anywhere until we install security update KB3033929, download, install.

And we get a Microsoft slap in the form of a message:



We violently throw a very sharp stone in the direction of Microsoft, download the old Daemon Tools from the torrents, successfully unpack Visual Studio, install, finally (00:00) compile MVP, we get the same error. Well, the version was good, but it didn’t grow together.

CHAPTER 11. On the threshold


We write to the second programmer, who at this moment urgently completes the server and the registration procedure. He recalls that there is a git repository , which on NT connects this library and works with it.
Suspiciously looking at the repository, download it, compile and run. Works.



Look at the code even more suspiciously. The code is identical, except that it is written in C ++ and not C.
We understand that the language has nothing to do with it. We look at the Sberbank libraries, which are pulling the code.
We see the last commit.

And here we are waiting for another surprise.

It turns out that the versions of the Sberbank library may be different. The last commit increases the version from 23 to 27. We copy to our test computer the version from the book - WORKS!

We check all the archives sent by Sberbank, compare versions and build a label:
Version Works
26.0.15 - Primary no
27.4.12 - From the repository yes
23.0.13 - From the repository yes
29.0.9 - The latest from Sat yes
23.0.13 - With the Kripter system patch yes

Ok, now we will live. On those systems where it costs 26, we will upgrade to 29 or 27 and everything will take off.
We throw a stone # 9 in the direction of Sberbank for having broken behavior on NT systems.

CHAPTER 12. What was waiting for them inside


Not enough "e" file? It does not matter, we take patched headers, dynamically link to the library to correctly return an error, write a code that simply writes the return code from the function to the file “e”, let's call the binary sb_pilot.exe and ...

Work, then it works.

Here is the version for the system "Kriptera" does not create a "p" file.

Sadly we look at blood dripping on our knuckles and a dent in the wall.

To begin with, what is the system "Crypter".

Cryptera is a Danish company that produces encryption equipment/security equipment/keys, etc. I think you all saw one of the copies of their products:



So, Sberbank uses their crypto module for pinpads and releases a special “patched” library, in which, as we already understood, the file “p” is not created. We write about this in Sberbank and in a few days we will receive the answer that “under the original system, the file“ p ”will be created, but under the patched system on the Crypter - no”. We will issue them a stone # 10 in a few days, because you need to work now.
Fortunately, or unfortunately, the functions that we use to carry out operations return the structure already mentioned:

  struct auth_answer {
  int TType;/** & lt;  [in] transaction type.  see :: OpetationTypes */
  unsigned long Amount;/** & lt;  [in] amount in kopecks */
  char RCode [3];/** & lt;  [out] authorization result code */
  char AMessage [16];/** & lt;  [out] text authorization result */
  int CType;/** & lt;  [in, out] card type */
  char * Check;/** & lt;  [out] check image, GlobalFree should be released in the calling program */
 };  

Oh, great, the check is already there, we can save it to a file on our own or directly output it to JSON ...

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

And we get the crash of the application due to treatment by invalid pointer.

CHAPTER 14. Fire and water


4:00 We execute Seth Bandha Sarvangasana to calm down, and carefully read the manual:
The
[out] check image should be released by GlobalFree in the calling program.
What does this give us? A lot of things. First, once the pointer needs to be cleaned with GlobalFree, it is sallocated with GlobalAlloc . Therefore, it does not issue a pointer to memory, as it was in the 16bit version, but an object number with the semantically declared type HGLOBAL, which can be fed in the GlobalSize function to get the size of the selected block and GlobalLock to block the piece of memory, but get the original pointer. By the way, Stone # 6 in the direction of Microsoft for NIH malloc and free, which are in the standard library.

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

And still get the fall. Okay, so what is GlobalSize? Zero? Kind of weird.

We check other functions that should also give a slip - we see the same picture.

It comes to mind that it is possible to independently generate a slip according to the data that the coolest payment function can issue (yes, Sberbank’s functions are called card_authorize2..14, I won’t throw a stone for it):

  struct auth_answer14 {
  auth_answer ans;/** & lt;  [in, out] The main parameters of the operation.  See :: auth_answer */
  char AuthCode [MAX_AUTHCODE];/** & lt;  [out] Authorization code.  7 bytes*/
  char CardID [CARD_ID_LEN];/** & lt;  [out] Card ID.  25 bytes  For international cards, all symbols except the first 6 and the last 4 will be replaced with ‘*’. */
  int ErrorCode;/** & lt;  [out] Error code.  */
  char TransDate [TRANSDATE_LEN];/** & lt;  [out] Date and time of operation */
  int TransNumber;/** & lt;  [out] The operation number for the operas.  day, see the number on the check */
  int SberOwnCard;/** & lt;  [out] Sberbank card ownership flag */
  char Hash [CARD_HASH_LEN];/** & lt;  [in, out] SHA1 hash from the card number, in ASCII format with a zero byte at the end.  40 bytes. */
  char Track3 [CARD_TRACK3_LEN];/** & lt;  [out] third card track */
  DWORD RequestID;/** & lt;  [in, out] Unique operation number.  Only PCI DSS solutions. */
  DWORD Department;/** & lt;  [in] Sequence number of the department from 0 to 14, inclusive.
  When setting the department number to 0xFFFFFFFF, the department number
  will be requested through the terminal interface after inserting the card.
  If the department number is specified outside the configured range,
  then the terminal will return error code 4191. */
  char RRN [MAX_REFNUM];/** & lt;  [in, out] The operation reference number assigned by the host.  Used by
  for operations return, multiple authorization and completion of the calculation.
  Contains a unique 12-digit reference number.
  With pre-authorization this field is a weekend.
  (it is filled by the pilot_nt.dll library), while
  the completion of the calculation is input (the value must
  be filled by the calling program;  it should
  match the value returned during the pre-authorization). */
  DWORD CurrencyCode;/** & lt;  [in] International currency code (810, 643, 840, 978, etc.) */
  char CardEntryMode;/** & lt;  [out] Card reading method ('D'-magn.band,' M'-manual input, 'C'-chip,' E'-non-contact EMV, 'R'-non-contact magstripe,' F'-fallback) */
  char CardName [MAX_CARD_NAME_LEN];/** & lt;  [out] Card type name */
  char AID [MAX_AID_ASCII_LEN];/** & lt;  [out] Application ID of the chip card (already in the form of an ASCIIZ string) */
  char FullErrorText [MAX_FULL_ERROR_TEXT];/** & lt;  [out] Full text of the error message */
  DWORD GoodsPrice;/** & lt;  [in] Unit price, cop (34.99- & gt; 3499) */
  DWORD GoodsVolume;/** & lt;  [in] Quantity of goods, in thousand shares (30.234- & gt; 30234) */
  char GoodsCode [MAX_GOODS_CODE + 1];/** & lt;  [in] Product code in the external system. */
  char GoodsName [MAX_GOODS_NAME];/** & lt;  [in] Product name in the external system.  Attention!  In the auth_answer14 structure, the product name is one character shorter than the gate.dll TGoodsData.  Fix this error as standard */
 };
 
 /** @brief Card operations
  * @param [in] track2 card track data with a magnetic strip.  If NULL, it will be suggested to read the card.
  * @param [in, out] auth_answer see :: auth_answer14
  * @param [in, out] payinfo Information for the payment system
  * @return int Error code.
  */
 PILOT_NT_API int card_authorize14 (
  char * track2,
  struct auth_answer14 * auth_answer,
  struct payment_info_item * payinfo
 );  

We try to select fields ... We find out that only one thing separated us from happiness - the Last Name and First Name of the card carrier. Without them, the slip is not considered to be a>:
Details: ID of an ATM, electronic terminal or other technical device intended for making transactions using payment cards; type of operation; transaction date; transaction amount; currency of operation; amount of commission authorization code; payment card details.
It is a pity, but to form a legitimate slip with the data that we have will not work.

Rummage through the documentation again.

Find an example that Sberbank delivers in the "examples" directory

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

Just displays the text, located on the index. But after all, we have already seen that it does not work like this ... Just in case, let's compile their example and run. Flight on line `file & lt; & lt; argument.auth_answ.Check; `well, Sberbank, hold stone # 11 for broken examples.

Seven o'clock You can already write to the developers of another wrapper, which several years ago was written in Delphi. We get the answer that everything works for them. We look for the base of their wrappers and find it on github :

  TAuthAnswer = packed record
  TType: integer;
  Amount: UINT;//IN Amount of operation in kopecks
  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);  

Simple type conversion to pointer without any function calls.

We begin to suspect evil spirits.

CHAPTER 17. Thunderstorm erupted


People are starting to return to the office, nodding sympathetically. PO doesn’t look very funny after hearing the latest news.

Here I recall one detail. When we displayed the fields of structure # 14 to see their values, then one byte of each line was cut off. On the one hand this, on the other
Warning! In the auth_answer14 structure, the product name is one character shorter than the gate.dll TGoodsData. Fix this error as standard
Maybe it’s still related to ...

A terrible guess overshadows the brain like lightning. Declare the structure as

  typedef struct __attribute __ ((packed)) {
  int TType;/** & lt;  [in] transaction type.  see :: OpetationTypes */
  unsigned long Amount;/** & lt;  [in] amount in kopecks */
  char RCode [3];/** & lt;  [out] authorization result code */
  char AMessage [16];/** & lt;  [out] text authorization result */
  int CType;/** & lt;  [in, out] card type */
  char * Check;/** & lt;  [out] check image, GlobalFree should be released in the calling program */
 };  

And ...

Nothing changes.

All the same, Size = 0, Still Lock = NULL.

Pain.

Tlen.

Involuntarily you are looking for a comfortable beam on the ceiling with your eyes, such as to withstand the weight. After so many non-stop hours of coding and studying the documentation, slender rows of bytes float before your eyes. And what if bytes are returned, which are returned at all?

  u32 i;
  for (i = 0; i & lt; sizeof (answ); i ++) {
  printf ("% 02x", * ((u8 *) & amp; answ + i));
  }
  printf ("\ n");
 
 
 C: \ banks \ sber \ sb_pilot & gt; 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 00 00 00 00 00 f8 6c 7a 00 00  

`30 00 00 ce` - which means that Sberbank still uses Packed structures. That's just in the headers about this is not a word. Therefore, the examples do not work, so it is impossible to get a pointer to the text at the end - because it is broken due to a shift of 1 byte. A huge and spiny stone in the direction of Sberbank!

And then one maaaalen little thing caught my eye. 4 + 4 + 3 + 16 + 4 + 4 = 35. And then there are 36 bytes, Obelix.

Once it is 36 bytes, the compiler still aligns the structure. So an extra byte is still inserted between RCode and AMessage. But why? After all, we have specified `__packed__`!

CHAPTER 18. Reverse


The reasons that the alignment is still included appeared in 2012: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 . A bug was fixed only in GCC 8 (a stone for 6 years of failure!), Which is not yet possible to upgrade to.Fortunately there is a workaround:

  - mno-ms-bitfields  

We will not analyze now the mechanism of this flag, just pass it to the compiler:



Slip! Darling! I missed you, I won’t even swear because of krakozyabr, I already threw a stone for it.

And finally, we’ll feed Microsoft a stone for giving GlobalSize/Lock zeroes to non-valid pointers.

CHAPTER 19. Last Chapter


To minimize the number of ifdefs for the sb_pilot interlayer, we wrote a separate application that completely simulates the linux version of sb_pilot. Thus, leaving the code of layer # 1 the same, leaving only one condition:

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



Battle Results:

  • Sberbank: 12 stones
  • Microsoft: 7 stones
  • GCC: 1 stone

Achivka-memory on our command board:

Source text: [From the sandbox] Sberbank or back and forth