
第1章。意外的客人
这一切都是从不幸的早晨开始的,当时项目经理宣布应将项目实施的最后期限迅速而果断地减少一个月。 更准确地说,该项目应在4天内准备就绪。 不,我们的订单不是野兽,它看上去完全不像
猫头鹰 (也许有点像乌鸦),只是发生了。 好吧,如果有必要,那是有必要的,尤其是因为向该团队(我是“ C”团队的主要开发人员)承诺要提供美味的东西。 时间和日历是星期四,11:00,到星期一,项目应该已经准备好了。
首先,我们到底在做什么。 我们从事电影院的自动化-设备的自动和远程控制,电影放映,监控,视频面板的自动化,现在还包括用于售票和酒吧的终端。 具体而言,最后一段专门针对本文。
该项目本身必须在星期一之前完成,它是Scala上的主服务器和铁付款终端VeriFone VX 820之间的一种层(实际上,还有更多的终端,但仅以它为例)。 显然,没有人会像这样给我们提供交易,因此使用了Sberbank / Arcus和UCS的实用程序和库。 因此,工作方案应如下:

从外部看,它看起来像这样:

同样,此子系统应在每个收银员在任何电影院看到的标准收银机上使用。
根据内部传统,我们将团队的每个项目都命名为Old Norse神话,为该子系统选择了名称Gefjon-生育力和生育力的女神的名字(对支付服务器来说是个好名字,不是吗?好吧,关于公牛切断该岛的传说非常适合当前的体系结构,用高级语言切断设备的工作)。
传入和传出消息的格式是带有JSON负载的HTTP服务器。 这是Scala(很难从套接字流中隔离二进制数据)和C(很难通过网络传输对象)之间的最佳折衷。 不需要进行许多可能的操作:付款,取消,退货,各种类型的报告,打开服务菜单和ping。 看起来没什么复杂的。 由于存在三种银行系统(并且预计以后会补充家庭资金),因此决定将项目分为以下几个部分:

绿色块是我们需要制作的,蓝色块是不能更改且银行提供的。
由于主要问题仅是来自Sberbank的软件引起的,因此本文作为一个整体将专门讨论陷阱,我们将其计入船上。
第二章烤羊肉

(照片:heaclub.ru)
...看起来像这样。 这个原型的代码看起来是一样的,该代码是几个月前编写的,目的是使所有更高层次的人都清楚我们可以使用银行应用程序。
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
显然,这不适合生产版本,因此本质上有必要重新编写所有内容。
每个提供用于终端的库的银行通常提供两个连接选项:通过库函数(.so / .dll)或通过仅需传递两个值的现成实用程序-操作类型和金额(必要时)。 从理论上讲,没有什么复杂的,只是一些
char buffer[100]; sprintf(buffer, "%d %d", atoi(argv[1]), atoi(argv[2])); system(buffer);
操作结果将被放置在文件“ e”中,而滑动检查将被存储在文件“ p”中。 只需将这些文件发送到stdout并转换为JSON,这样HTTP服务器就可以将它们作为有效负载发送出去,而无需考虑其中的内容。
但是,如果一切都这么简单,那么就不会发表这篇文章。
第4章。越过山脉和越过山脉
该实现的最初版本是一个简单的应用程序调用-HTTP服务器称为具有统一参数的必需包装器(例如,X-report为4),实用程序(例如gfj_pilot)使用该操作所需的参数启动了sb_pilot(例如,X-report为9)。 。 然后包装器实用程序从电子文件中读取操作结果(例如2000-“付款被拒绝,重复操作”)并将其转换为通用错误(例如3-“读取或处理卡/帐户时出错,重复操作”)。 之后,“ p”文件被转换为base64,以避免格式破坏,并将结果与结果一起以JSON的形式发送到stdout。
所有这一切都工作得很好,直到一个好时刻我们被告知...
...这在Windows下不起作用。

好吧,更准确地说,Windows本身没有问题(除了转帐以Cp-1251编码生成,并且控制台可在CP866上运行)。 根本没有生成“ e”文件。 直接启动银行业务实用程序:
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>
确实,没有“ e”文件。 通往Sberbank#1。 我们正在写信给Sberbank(随后我们收到了应该是的答复),并且由于没有时间进行通信并且有必要立即开始,因此我们正在寻找获得结果的解决方法。
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
是的,可以从日志sbkernel.log获得结果。 不方便的是,再也没有地图哈希值可以随后从Sberbank搞定“谢谢”。 不好
您将必须连接到pilot_nt.dll库并从中导入函数。 一切都会好起来的,但是……通向Sberbank#2:在Linux下没有这样的库,您将不得不为不同的平台创建两个不同的应用程序-对于Linux,调用sb_pilot实用程序(类似于loadparm.exe,顺便说一句,在不同平台上为不同的实用程序名称命名为3) ),在Windows下连接到pilot_nt.dll库。
第5章。黑暗中的谜语
在19:00时。
Sberbank是一家大公司,大多数软件解决方案都是根据GOST和正式文件制作的。 我们进入Sberbank随图书馆提供的目录:
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
很好,但是我们只对开发人员感兴趣的目录:
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
为了以防万一,大量废纸重新读取了pilot_nt,从中我们学到了以下内容:
表1.支持的sb_pilot操作系统。
事实证明,用于Windows的实用程序仍应称为sb_pilot。 好吧,它是Sberbank#4的石头,因为它本身的文档不一致。
传输程序结果。
在程序结束时,形成了两个文本文件-交换文件和检查文件。
第一个命名为e,旨在将完美操作的参数传递给调用程序。 该文件的第一行包含操作结果的代码,以及解释文本消息的逗号分隔文本。 代码0表示成功付款,任何其他值-拒绝付款或无法付款。

懒洋洋地,我们又扔了一块石头,开始研究直接连接图书馆的文档。
调用库函数的过程
用信用卡支付(返还)购物时,现金程序必须通过填写TType和Amount字段并在其余字段中指定零值来从Sberbank库调用card_authorize()函数。 在函数的最后,您需要分析RCode字段。 如果它包含值“ 0”或“ 00”,则认为授权已成功完成,否则被拒绝。 另外,您必须检查“检查”字段的值。
如果不是NULL,则必须将其发送以打印(在非财务模式下),然后
通过调用函数GlobalFree()删除。 关闭班次时,现金程序必须通过填写TType = 7字段并在其余字段中指定零值来从Sberbank库中调用close_day()函数。 在功能结束时,检查“检查”字段的值。
如果“检查”字段不为NULL,则必须将其发送以打印(在非财务模式下),然后通过调用GlobaFree()函数将其删除。
听起来很容易,即使提供了头文件也是如此。 好吧,将其插入,编译并...
$ 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; ^ pilot_nt.h:544:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:567:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:590:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:627:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:668:3: error: unknown type name 'auth_answer' auth_answer ans;
嗯...什么? 打开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] */ };
马上,不用寻找在CP1251编码中的俄语注释。
好吧,最严肃的事情是:亲爱的C ++开发人员。 如果编写extern“ C”,则意味着该块内部的代码必须由C编译器编译。 如果尚未对结构进行“ typedef”定义,则每次将其作为类型引用提及时,都必须编写关键字“ struct”。
为开发人员修补文件,在需要的地方用单词“ struct”代替。 链接到`pilot_nt.dll`库。 胜利不? 我们启动我们的应用程序。
第6章:从火到火焰
好吧,你明白了吧? 该应用程序崩溃了。 直到主要。 思考,为Windows添加errno函数的NIH模拟:GetLastError(针对Microsoft的第3号石头,前两个用于编码)。
C:\banks\sber\WIN>sb_pilot.exe 1 1000 E: !g_sblibrary (0xc0000096)
0xc0000096? GetLastError不应该返回足够的错误代码吗?
有关操作系统提供的错误代码的完整列表,请参阅系统错误代码。
是的,在
这里打开文章:
以下主题提供系统错误代码的列表。 这些值在WinError.h头文件中定义。
- 系统错误代码(0-499)(0x0-0x1f3)
- 系统错误代码(500-999)(0x1f4-0x3e7)
- 系统错误代码(1000-1299)(0x3e8-0x513)
- 系统错误代码(1300-1699)(0x514-0x6a3)
- 系统错误代码(1700-3999)(0x6a4-0xf9f)
- 系统错误代码(4000-5999)(0xfa0-0x176f)
- 系统错误代码(6000-8199)(0x1770-0x2007)
- 系统错误代码(8200-8999)(0x2008-0x2327)
- 系统错误代码(9000-11999)(0x2328-0x2edf)
- 系统错误代码(12000-15999)(0x2ee0-0x3e7f)
好的,我们出现了一个未记录的错误,我们扔了石头,打开了全知的Google:
错误的本质在于某些子例程使用了指令之一
- _inp()
- _inpw()
- _inpd()
- _outp()
- _outpw()
- _outpd()
NT内核尝试直接使用并行端口,因此在NT内核下禁止使用该功能。 显然,此代码在库初始化程序中被调用,即 启动时,库需要轮询设备端口,但是NT内核需要通过驱动程序来工作。
绝望的情况?
第8章蜘蛛和苍蝇
晚上10点 为了以防万一,产生了验证这不是由于我们使用mingw与Linux交叉编译的事实。 同时,我们了解到Sberbank仅提供32位应用程序,因此它不能与64位应用程序一起使用,好的,但是无论如何,我们将在2019年向Sberbank推出仅32位版本的解决方案。
给定 :安装在virtualbox Windows 7中;
必需 :安装Visual Studio并复制MVP。
我们转到Microsoft网站,下载了Visual Studio 2017.我们获取社区许可证,因为我们将其用于验证,因此对于企业来说,许可证将在起飞时购买。
下载几百兆字节并...
我们看到不支持我们的操作系统版本(Windows 7)。
好的,我们转到各种淫秽网站,我们正在寻找Visual Studio 2008,再次下载数百兆字节,然后...
我们得到了iso文件。
好的,让我们尝试安装Daemon Tools 10(因为这是该站点提供的版本)以插入该虚拟磁盘。
运行下载的二进制文件。 Misfire,需要.NET Framework 4.5,下载,安装。
我们开始下载二进制文件,安装已经开始,引导程序说它需要4.5.2,下载,安装。
我们启动下载的Binar,安装已经开始,引导加载程序说在安装安全更新KB3033929之前,它不会在任何地方进行安装,下载,安装。
作为消息,我们得到了微软的耳光:

我们猛烈地向Microsoft投掷了尖锐的石头,从torrent下载旧的Daemon Tools,成功解压缩Visual Studio,安装,最后(00:00)编译MVP,我们得到了同样的错误。 嗯,有一个很好的版本,但是没有一起发展。
第十一章
我们正在写给第二位程序员,他目前正在紧急完成服务器和注册过程。 他回忆起有一个git
仓库可以连接到NT上的这个库并使用它。
怀疑地查看存储库,下载它,编译并运行它。 可以用

我们更怀疑地看代码。 该代码是相同的,除了它是用C ++而不是C编写的。
我们知道语言与它无关。 我们看一下代码提取的Sberbank库。
我们看到最后一次提交。
在这里,我们正在等待另一个惊喜。
事实证明,Sberbank库的版本可能不同。 最后一次提交将版本从23增加到27。 将版本从Gita复制到您的测试计算机-工作!
我们检查了Sberbank发送的所有档案,比较版本并构建了平板电脑:
太好了,现在让我们al愈。 在那些花费26的系统上,升级到29或27,一切都会起飞。
我们向Sberbank投掷石块9,以破坏NT系统上的行为。
第十二章里面等待着他们的是什么
电子文件不足? 没关系,我们采用补丁标题,动态链接到库以正确返回错误,编写将函数的返回代码简单地写入“ e”文件的代码,我们将biner sb_pilot.exe称为...
要工作,就行。
那只是“ Cryptor”系统的版本,不会创建“ p”文件。
我们可悲地看着滴落在指关节上的血和墙上的凹痕。首先,什么是Cryptor系统。
Cryptera是一家丹麦公司,生产加密设备/安全设备/密钥等。我想你们都看到了他们产品的副本之一:

因此,Sberbank将其加密模块用于密码键盘,并发布了一个特殊的“补丁”库,如我们已经了解的那样,其中未创建“ p”文件。 我们为此写信给Sberbank,几天后我们就会得到答案,“在原始系统下,将创建文件p”,但在加密后的Cryptor下将不会创建。” 几天之内,我们将给他们10号石,因为您现在需要工作。
幸运的是,我们用来进行操作的功能返回了已经提到的结构:
struct auth_answer{ int TType; unsigned long Amount; char RCode[3]; char AMessage[16]; int CType; char* Check; };
哦,很好,支票已经存在,我们可以将其保存到文件中或立即将其打印为JSON ...
printf("%s\n", answer.Check);
而且由于访问无效的指针,我们使应用程序崩溃。
第十四章:水与火
凌晨4点 我们执行Seth Bandha Sarvangasana进行镇定,并仔细阅读该手册:
支票的[out]图像,应由GlobalFree在调用程序中释放
这给了我们什么? 很多 首先,由于需要使用GlobalFree清理指针,因此使用
GlobalAlloc对其进行了分配。 因此,它不会像在16位版本中那样发出指向内存的指针,而是发出具有语义声明的类型HGLOBAL的对象号,可以将其提供给GlobalSize函数以获取分配的块的大小,而GlobalLock可以将其馈入以阻塞一块内存,但是可以获取原始指针。 顺便说一下,NIH malloc和free排在Microsoft的第6个标准库中。
printf("%s\n", GlobalLock(answer.Check));
并且仍然下降。 好的,GlobalSize显示什么? 零吗 不知何故。
我们检查其他应该也要注意的功能-我们看到的是同一张图片。
我想到,我可以根据最酷的支付功能提供的数据自行生成发票(是的,Sberbank拥有名为card_authorize2..14的功能,对此我不会一掷千金):
struct auth_answer14 { auth_answer ans; char AuthCode[MAX_AUTHCODE]; char CardID[CARD_ID_LEN]; int ErrorCode; char TransDate[TRANSDATE_LEN]; int TransNumber; int SberOwnCard; char Hash[CARD_HASH_LEN]; char Track3[CARD_TRACK3_LEN]; DWORD RequestID; DWORD Department; char RRN[MAX_REFNUM]; DWORD CurrencyCode; char CardEntryMode; char CardName[MAX_CARD_NAME_LEN]; char AID[MAX_AID_ASCII_LEN]; char FullErrorText[MAX_FULL_ERROR_TEXT]; DWORD GoodsPrice; DWORD GoodsVolume; char GoodsCode[MAX_GOODS_CODE+1]; char GoodsName[MAX_GOODS_NAME]; }; PILOT_NT_API int card_authorize14( char *track2, struct auth_answer14 *auth_answer, struct payment_info_item *payinfo );
我们正在尝试选择字段。我们发现只有一件事使我们与幸福分开了-持卡人的姓氏和名字。没有它们,单据不合法:详细信息:用于使用支付卡进行交易的ATM,电子终端或其他技术手段的标识符;操作类型;交易日期;交易金额;交易货币;佣金金额授权码;付款卡详细信息。
遗憾的是,但是要对我们拥有的数据形成法律效力,将无法正常工作。让我们再次研究文档。我们在“示例”目录中找到Sberbank提供的示例 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;
它仅显示指针上的文本。但是我们已经看到它不能那样工作……以防万一,我们将编译他们的示例并运行它。离开行“ file << arguments.auth_answ.Check;”,好吧,Sberbank,将第11号石作为无效示例。早上7点 您已经可以写信给几年前用Delphi编写的另一个包装器的开发人员。我们得到的答案是一切对他们都有效。我们正在寻找其包装的基础,并在github上找到: TAuthAnswer = packed record TType: integer; Amount: UINT;
简单类型的指针转换,无需任何函数调用。我们开始怀疑恶魔。第十七章雷暴爆发
人们开始回到办公室,同情地点头。PO不太高兴知道最新消息。在这里,我记得一个细节。当我们显示结构#14的字段以查看其值时,每行一个字节被切除。一方面,另一方面注意!在auth_answer14结构中,产品名称比gate.dll TGoodsData中的字符短一个字符。修正此错误为标准
也许它仍然与……有关。一个可怕的猜测像闪电一样在大脑中冒出。将结构声明为 typedef struct __attribute__((packed)) { int TType; unsigned long Amount; char RCode[3]; char AMessage[16]; int CType; char* Check; };
而且...什么都没有改变。静止大小= 0,静止锁定= NULL。痛衰变您不由自主地用眼睛在天花板上寻找舒适的横梁,以使其可以承受重量。经过无数小时的编码和研究文档,细长的字节行浮现在我们的眼前。但是,如果我们打印通常返回的字节怎么办? 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”-表示Sberbank仍使用打包结构。但是标题中对此一无所知。因此,这些示例不起作用,因此,不可能在末尾获得指向文本的指针-因为该文本由于移位1个字节而损坏。通往Sberbank的巨大而多刺的石头!然后一个maaaalenky的细微差别引起了我的注意。4 + 4 + 3 + 16 + 4 + 4 =35。这是36个字节,Obelix。如果有36个字节,则编译器仍在对齐结构。因此,在RCode和AMessage之间仍然插入了一个额外的字节。但是为什么呢?毕竟,我们指出了“ __packed__”!第十八章返程
仍然保持对齐的原因出现在2012年:https : //gcc.gnu.org/bugzilla/show_bug.cgi?id=52991。一个错误仅在GCC 8中被修复(一块石头可以塞满6年的马车!),尚无法更新。幸运的是,有一种解决方法: -mno-ms-bitfields
现在我们不讨论这个标志的机制,只是把它交给编译器:
滑!亲爱的!我想念你,我甚至都不敢发誓,因为我已经扔石头了。最后,我们给了微软一个石头,因为GlobalSize / Lock给无效指针赋予零。第十九章最后一章
为了最小化sb_pilot层的ifdefs数量,我们编写了一个单独的应用程序,该应用程序完全模仿sb_pilot的Linux版本。因此,使第1层代码相同,仅保留一个条件: #if defined(BXI_OS_GLX) #define GFJ_PILOT_EXECUTABLE "./sb_pilot" #elif defined(BXI_OS_WIN) #define GFJ_PILOT_EXECUTABLE "./sb_pilot.exe" #endif
战斗结果:- Sberbank:12石头
- 微软:7石
- GCC:1石头
在我们的命令板上记录成就: