课程的故事

你好 我想谈谈我的课程或好奇心会导致什么。


长期以来,我什么都不做,我在Symbian下编写程序。 在组装过程中,我有时会遇到一些奇怪的事情。 一切都指向elf2e32实用程序。 它的任务是将elf格式的输入二进制文件转换为另一个特定于Symbian的文件-e32图像。 好奇心使我迷惑了很长时间-该实用程序是如何工作的,为什么有时会出现故障? 不久之后,另一个问题开始困扰我-课程工作的主题=)我决定将商务与娱乐相结合,并下载了其源代码。 走开...


第一次提交不会


第二个-我们包含非标准的gcc扩展,从源代码中添加缺少的类,函数和常量。 拍摄对象快乐地跌落。 但是进步。 我们从调试器开始-将调试器放入一个仅初始化另一个的类,该类又初始化下一个。 一个真正的功能! 快进来 哎呀 我们在哪里? 停止调试器! 外科医生! 手术刀! 酒! 黄瓜! 附录f火箱类! 给nullptr而不是NULL! 我们有C ++ 14! 哇,用零初始化所有东西的恐怖构造函数! 然而,一次又一次-但对于我们来说,C ++ 14要求对类进行默认初始化! 现在什么都简洁了...


知道了 我们一次修复尽可能多的问题。 我弄清楚了为什么调试器会像合适的那样跳到源代码-非洲人在抽象上大放异彩,从UseCaseBase类继承了80级的继承:)然后,Message&ParameterManager类的静态实例的构造函数类从我的眼中飞了出来。 辛格尔顿·迈尔斯(Singleton Myers)? 不,没有听到。 火箱抽象! 万岁革命! 万岁POD !!!


哇 这棵树长得多么有趣。 主要工作由BuildAll()函数完成。 如果指定了所有参数,则函数将收集导入库,一个文件,该文件指定函数和变量的名称以及它们在导入库中的可用顺序以及二进制文件本身。 UseCaseBase的所有后代都通过重载更改了其算法。 有时在后代中,我们准备辅助数据,但大多数情况下,他们只是关闭了某些文件的创建。 例如,未指定用于组装某些东西的文件名-创建了一个新类。 蠢货 如有必要,中断此类收集器功能的执行就足够了。 很容易理解我的动作B-)


我们继续删除空类,将null替换为nullptr_t,将范围迭代器替换为for(auto x:*)。


我们修复了处理命令行参数中的错误。


必须使用静态分析器检查代码。 从哪里开始? 嗯,在XP下,选择范围很小-cppcheck,并且代码块开箱即用地支持它。 哇,真是太好了! 甚至有删除char []! 该死,我知道空闲RAM的一半去了哪里=)


因此,我们添加了从精灵文件libcrypto.dll生成的文件以及描述其创建命令行参数的文件本身。


哎呀 CPPCheck错误...必须是(a || b)...


我将尝试在Visual Studio 15中进行构建,而Win10会坚持使用。 放在虚拟机上。 完成,下载并运行在线Studio安装程序。 什么啊 不想将跳转保存到主机共享文件夹吗? 是的,on死你了! 下载您的授课地点...现在,我们将下载的内容转移到该文件夹​​并开始安装。 什么啊 再次忽略共享文件夹??! 是的,on死你了! 成为你被教导的地方...


原则上,在一个核心和3个演出框架上有十二个沙沙作响。 工作室到工作室! 周到,但时间不长。 我们在工作室中打开我的项目,然后在文件夹中发誓...已经有多少? 是的,您cho死了...我们收集并宣誓使用非标准扩展STL hash_set。 远程的。 远程??? 打开大脑=)
哇,多么吸引人的代码:


int ElfFileSupplied::UnWantedSymbolp(const char * aSymbol) { static hash_set<const char*, hash<const char*>, eqstr> aSymbolSet; int symbollistsize=sizeof(Unwantedruntimesymbols)/sizeof(Unwantedruntimesymbols[0]); static bool FLAG=false; while(!FLAG) { for(int i=0;i<symbollistsize;i++) { aSymbolSet.insert(Unwantedruntimesymbols[i]); } FLAG=true; } hash_set<const char*, hash<const char*>, eqstr>::const_iterator it = aSymbolSet.find(aSymbol); if(it != aSymbolSet.end()) return 1; else return 0; } 

让我们考虑一下...瞧!


 int ElfFileSupplied::UnWantedSymbolp(const char * aSymbol) { int symbollistsize = sizeof(Unwantedruntimesymbols) / sizeof(Unwantedruntimesymbols[0]); for (int i = 0; i<symbollistsize; i++) { if (strstr(Unwantedruntimesymbols[i], aSymbol)) return 1; } return 0; } 

我的美丽...


因此,如果此标志设置不正确或根本没有设置,为什么程序会引发异常? 您为何如此残酷,美丽而遥远...让我们将该标志重置为安全值。 这个标志也很好...这个,那个和这些。 还是最好将其放在单独的函数中? 好主意! 我们称之为ParameterManager :: CheckOptions()!


向左走是跌倒,向右走是未捕获的异常,跳到了位-至少要感谢BSOD =)


无聊...毛刺和曲率...


Olya-la !!! 在STL上模拟CleanUpStack Symbian吗?
基本上没有什么特别的:


 std::vector<char*> cleanupStack; 

清洁:


 std::vector<char*>::iterator aPos; char *aPtr; aPos = cleanupStack.begin(); while( aPos != cleanupStack.end() ) { aPtr = *aPos; delete[] aPtr; ++aPos; } 

一些明亮的头,而不是左/右使用左/右。 谢谢cppcheck。


啊,懒洋洋地在监视器前拆下cppcheck日志... github将为我们提供什么?..编码...我们连接了项目...我想了一下,就可以了! 现在,您可以阅读成功消息,以消除躺在沙发上的错误^^


因此,看来这不是越野车……让我们收集一些东西,例如libcrypto.dll。 尽管未压缩的文件比实用程序从SDK创建的字节多一百个字节,但它仍然有效。 接下来,将不断比较此版本的实用程序和从SDK创建的二进制文件。 命令行选项本身是相同的。
腊肠犬,在哪里获得二进制文件的差异模拟? 嗯,把脚本放到活塞上。 信息太多-您需要简单得多的东西。 pdf / djvu识别dll-AlternateReaderRecog.dll是一个不错的选择,排气量小于4 KB。 腊肠犬,偏移量在导入部分中有所不同。 我们在十六进制编辑器中打开它们。 开头是一样的,在我的版本中,垃圾邮件的位置更远,就在原始版本中该节的结尾之后。 但是在我的版本中,下一部分将在100字节后开始。 通过相同的字节值,文件会有所不同! 偏移量进一步指示正确的地址...二进制是正确的! 啊!


一个月后。 那么这一百个字节是从哪里来的呢?
好吧,由于尚不清楚它是如何工作的,我们开始打破创建E32Image的算法。 继续模拟AlternateReaderRecog.dll。 我们在输出处增加二进制文件的大小-没办法,覆盖memset节-没办法,减小二进制文件的大小-没办法。 rr 什么他妈的! 我打破了发行版中的内容,然后开始调试? 嗨,韧皮,重新开始。。。Soooo部分被淘汰-好的! 增加了二进制文件的大小! 好! 减小导入节的大小! 有!!! 字节与SDK中此实用程序的精疲力竭的相同部分相同!
我们研究用于创建此部分的代码。 “ sizeof(char *)”-Pvs-studio的开发者之一安德烈•卡尔波夫(Andrey Karpov)的文章中提到了一些问题,即类型可以占用不同的内存量-占用多少空间? MinGW-8字节,Visual Studio-4 !!! 我们将这8个字节分成两半,处理一些事情。 FFse! 代码部分如何? 此dll没有全局变量。 没有全局变量-没有节...让我们来做些更重的事情-libcrypto.dll。
我的实用程序输出中的文件现在小了100多个字节...什么? 导入部分的字节相同-很好。 代码部分-不?


肉眼看,我无法比较这样的文字墙...我会去寻找差异来进行字节比较...
几天后,我仍然可以通过Google搜索找到游戏。 vbindiff是一个具有ala Norton Commander界面的控制台实用程序,它在两个水平面板中显示了两个文件之间的差异。 要转到差异点,请按Enter。 好啊 您可以将两个文件拖到图标上进行比较,程序将打开它们! 太好了!
比较-soooo标头中的crc和创建时间不同。 没什么。 这里的字节不同,这里又是另外一百个……哇! 几十,几百,几千个字节的差异? 所以,看,看看它们属于哪个部分...我们看一下偏移量...是的,数据部分...
我们完成了技巧,就像导入部分一样。我们重置了内存集。 增加部分的大小...下降...增加。 提供调试器的帮助和帮助。 我们打开函数,从函数... Grr中创建部分-粥。
...啊,明天...直到我解决其他问题...


例如,我将添加测试,但情况如此混乱,以至于无法将程序分成小模块。 您无法将测试直接插入代码中-这样您就会了解辣根。 这个主意! 不断启动带有不同参数的程序-我一直在测试该程序……让我们更好地将它们全部用python编写成单独的脚本。 是的,很棒的主意,很棒。 报告测试错误,报告错误但不会崩溃时,脚本应继续工作。 做完了!


我们返回到公羊...这个函数调用这个,然后这个,我们去这里...那么,先生,它把我带到哪里了? gh,困惑…………明天,直到我解决其他问题……


这样两个月过去了...


该死,这段代码在哪里形成? 我必须休学假,所以至少我会和你一起解决! o 在这里,该部分的符号进入输入... printf将显示什么? 一切都不适合控制台缓冲区...让我们将尾气保存到文件中...好吧,到目前为止,没有什么特别的...停止! 相同的线! 很多相同的线! 从哪里来? 将printf添加到每个数据源(足够的耐心,占五分之三的公顷)。 是空的! 我们来看剩下的函数调用之一……Taak。 循环后的迭代器增量 和待办事项的编纂警告? 我们转移到周期。 启动!!! 有尺寸搭配! 有一个字节匹配! 固定! git blame拒绝给英雄起名...我们看原始照片-这不是我所做的。 还是对与诺基亚无关的开发人员来说是“炸弹”? rr


仔细检查排气测试,检查字节文件。 一切正常。 正在发行中!


奥莉亚! 是时候进行一次大清洗了!!! 是时候用根来根除UseCaseBase树了!!!
大多数后代已经累死了,我们将有用的函数移除到了生成器类上。 仅保留UseCaseBase及其后代ElfFileSupplied。 UseCaseBase-是处理命令参数并为ElfFileSupplied类声明几个纯虚函数的类的包装。 简而言之,不需要小提琴手...天是蓝色,好...再一个小时...我将在本课程中处理,您可以出去散步...呼吸空气,热身,很好...走吧! 因此,请注释掉此功能。 收集! 太棒了,您需要考虑如何精美地对其进行重做……完成!!! 下一个功能! 做完了! 下一个! 做完了! 做完了! 是的 是的 是的 最后一个功能... 组装后运行...工作加速了七倍? 排气是正确的...有趣。 调试版本也缩小了2米? 哇! 你可以散散步。 在晚上? Kaak ??? 我今天在哪里? 我将启动测试并放松...测试安静进行-您可以放松...


现在让我自己写点东西吧...哦,使用外部可用的函数和变量的类看起来令人毛骨悚然。 操作原理:从文件读取,解析行并保存到文件。 为了对这些线条进行分析,在S中分离出一整类精选的面条……Sooooh ...让我们想想……真是太美了:
我们读取了std :: getline()行,从行和parsim的边缘删除了空格。


待续...源代码-https://github.com/fedor4ever/elf2e32

Source: https://habr.com/ru/post/zh-CN430794/


All Articles