Babel HelloWorld

由于一个偶然的契机,笔者接触到了一个 js 处理神器 ———— babel,可以用来方便快速的处理 js 代码,实现自定义功能。笔者花了几天的时间对 babel 的实现原理和使用方法进行了简单的梳理,特此记录,以备之后的学习。

babel 的官方网站上有着较为完备的文档,其 GitHub 上也有各个语言版本的详细使用手册,笔者在学习过程中主要参考了以下两个网站。

https://www.sitepoint.com/understanding-asts-building-babel-plugin/

https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-stages-of-babel

基础

Babel 是一个用于操作 js 的框架,它为用户提供了方便的接口可以通过插件自定义操作。

Babel 的主要思想就是通过操作 js 代码对应的 AST 树得到另一个符合语法的 js。

Read More

EVM Simulator

CTF 的时候遇到一个以太坊相关的题目,只有合约的 opcode,最后在纸上用手演算做出来的。当时就感觉如果有一个能直接调试 opcode 的东西就好了。在 Salt 大佬 的建议下,花了一天的时间改了改 octopus,攒了一个简易的调试器。

https://github.com/Hearmen/evm_emulator

EVM emulator

这个调试器是基于 octopus 开发的。 octopus 是研究者 pventuzelo 开发的一款以太坊逆向工具,使用这个工具可以很方便的将大部分 ETH 上的 opcode 编译成 CFG

调试器为了方便,直接在 octopus 的源码上进行了修改,以后可能会把这一部分抠出来。

有了 octopus 的基础,调试器实现起来就非常方便。octopus已经将将 opcode 的执行流程进行了分析,基于这个分析我们只需要实现一个 stack,一个 memory 和一个 storage 即可。

Read More

ETH Varible Overlap

经过对 ETH 以及其运行时环境 EVM 的初步研究,我们在合约层面和虚拟机层面分别发现了一些问题,其中有些问题可能导致非常严重的后果,值得 ETH 智能合约的开发者注意。变量覆盖问题就是其中非常典型的一种,有很多以太坊安全研究人员都已经发现了这个问题,但是分析大都停留在结果层面,没有做进一步的探讨。本文将从变量覆盖这个问题入手结合 EVM 虚拟机和 Solidity 编译器的源码详细分析这个问题的表现,可能产生的影响,以及最终导致这个问题的 solidity 编译器根源所在。

变量覆盖

在某些合约中,我们发现在函数内部对 struct 类型的临时变量进行修改,会在某些情况下覆盖已有的全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pragma solidity ^0.4.23;
// A Locked Name Registrar
contract Locked {
bool public unlocked = false; // registrar locked, no name updates
struct NameRecord { // map hashes to addresses
bytes32 name; //
address mappedAddress;
}
mapping(address => NameRecord) public registeredNameRecord; // records who registered names
mapping(bytes32 => address) public resolve; // resolves hashes to addresses
function register(bytes32 _name, address _mappedAddress) public {
// set up the new NameRecord
NameRecord newRecord;
newRecord.name = _name;
newRecord.mappedAddress = _mappedAddress;
resolve[_name] = _mappedAddress;
registeredNameRecord[msg.sender] = newRecord;
require(unlocked); // only allow registrations if contract is unlocked
}
}

合约的源码如上面所示,在正常情况下,由于合约并没有提供修改 unlocked 的接口,因此不太可能达到修改它的目的。但是实际上我们在测试中发现,只要调用合约的 register 方法就可以修改 unlocked。

Read More

EOS 搭建指南

EOS 与 ETH 相比,其架构更加复杂,由于其主链刚刚上线不久因此 EOS 中的合约数量也不多,为了学习和理解 EOS 的相关知识,笔者跟随 EOS 官网 的教程对 EOS 进行了初步的了解,由于网上目前的资料参差不齐,因此将学习过程中遇到的问题记录在此,以备查阅。

EOS 源码编译

编译过程比较繁琐,网上也有很多教程,这里不再赘述,仅记一下笔者搭建过程中遇到的一些坑。由于 EOS 代码版本迭代十分频繁,因此网上许多基于旧版本 EOS 源码的搭建教程并不适用。这里推荐使用其源码中提供的脚本 eosio_build.sh 完成工作。这个脚本会根据编译时的实际环境调用不同系统的专用脚本来完成工作,笔者的环境是 Ubuntu_16 因此最后会调用 /scripts/eosio_build_ubuntu.sh 进行编译。其中比较重要的两个点是首先需要安装最新版本的 boost ,其次是需要安装 OpenSSL,安装完成之后一般来说就可以成功的编译起来了。

如果编译过程中提示系统硬件配置等信息不符合要求,如内存硬盘等等不够大,可以直接修改脚本中的判断条件。比如默认状态下脚本要求 EOS 编译的内存要有 8G ,但是如果我们在虚拟机中进行编译内存常常不满足要求,此时只需要修改用于进行硬件检测的编译脚本即可,将这个判断条件改小。

1
2
3
4
5
if [ "${MEM_MEG}" -lt 7000 ]; then
printf "\\tYour system must have 7 or more Gigabytes of physical memory installed.\\n"
printf "\\tExiting now.\\n"
exit 1
fi

Read More

Ethernaut Zeppelin 学习

https://ethernaut.zeppelin.solutions/ 网站上列出了一系列智能合约在编写过程中可能产生的问题,可以帮助初学者很好的了解智能合约

https://github.com/OpenZeppelin/ethernaut/blob/master/gamedata/descriptions/pages/help.md 帮助文档

花了一整天假装自己 AC 了所有的题目,这里把过程中的一点总结和经验记录一下,留以备忘

Hello

题目是用来帮助熟悉平台的。这里简单记录一下平台和 Remix 编辑器的基本操作

平台首先需要安装 MetaMask 插件并登陆账户,如果打开平台时没有登陆账户,那么登陆之后需要刷新页面使账户信息生效。

Remix 是非常好用的在线编辑平台,其不仅可以编译 solidity 代码生成自己的测试合约,也可以根据地址映射链上已有的合约

在顶部菜单中可以选择合约运行的环境,运行合约的账户。并且可以配置执行合约代码时所带有的 gas 和 value 相当于调用合约时的 .value().gas()

在执行了合约代码之后可以点击下方的 debug 按钮对合约代码进行调试

调试功能十分强大,可以查看运行时的栈,memory,opcode,等几乎所有的 EVM 关键信息

Read More

Bypass ACG With OpenProcess

https://bugs.chromium.org/p/project-zero/issues/detail?id=1435&can=1&q=&start=1200 p0 研究者 ifratric 提出的一种绕过 ACG 保护的方案,即通过修改同一 AppContainer 中的其他 MicrosoftEdgeCp.exe 进程的内存来阻止另一个进程开启 ACG 保护

ACG 的开启

进程的 ACG 需要调用函数 SetProcessMitigationPolicy.aspx) 或者 ZwSetInformationProcess 来开启。这里我们主要关注 SetProcessMitigationPolicy ,函数的原型如下

1
2
3
4
5
BOOL WINAPI SetProcessMitigationPolicy(
_In_ PROCESS_MITIGATION_POLICY MitigationPolicy,
_In_ PVOID lpBuffer,
_In_ SIZE_T dwLength
);

第一个参数指定要设定的缓解策略类型,第二个参数根据第一个参数指定不同的 Policy 数据,第三个参数指定第二个参数的长度。

Read More

MapViewOfSection and Shared-Memory

之前对于 UnmapViewOfFile 的分析中留了一个坑,即使用 SECTION 在进程之间共享内存的情况。拖了几个星期,终于把这个问题稍稍搞明白了一点。这里将相关的知识点记录一下。

问题描述

微软对于 UnmapViewOfFile 绕过 ACG 这种攻击方式的补丁是在 JIT 进程端移除了 VirtualAlloc/VirtualProtect 操作,从而切断了将其他属性内存转化为可执行页的路径。于是笔者产生了疑问:在不使用 VirtualAlloc/VirtualProtect 的情况下 Render 中的内存页是如何 commit 的

在打上补丁之后,内存的分配操作只剩下 AllocLocalView ,查看函数源码

1
2
3
4
5
6
AllocLocalView{
//...
process = GetCurrentProcess();
NtdllLibrary::Instance->MapViewOfSection(sectionHandle, process, &address, NULL, viewSize, &mapOffset, &viewSize, NtdllLibrary::ViewUnmap, NULL, flags);
//...
}

代码中调用 MapViewOfSection 在当前进程中为这个用来共享内存的 Section ,其中 CommitSize 参数为 viewSize。实际调试 Edge, 在这个调用上下断点 JIT 进程断在此处,此时 Render 中的虚拟内存地址已经由之前那次 Map 操作确定了,查看 Render 进程中对应的虚拟地址空间

1
2
3
4
5
6
7
8
9
0:001> dd 1ec`e6240000
000001ec`e6240000 ???????? ???????? ???????? ????????
000001ec`e6240010 ???????? ???????? ???????? ????????
000001ec`e6240020 ???????? ???????? ???????? ????????
000001ec`e6240030 ???????? ???????? ???????? ????????
000001ec`e6240040 ???????? ???????? ???????? ????????
000001ec`e6240050 ???????? ???????? ???????? ????????
000001ec`e6240060 ???????? ???????? ???????? ????????
000001ec`e6240070 ???????? ???????? ???????? ????????

执行这次函数调用,再次查看 Render 进程地址,发现此时对应的虚拟地址已经映射了物理地址

1
2
3
4
5
6
7
8
9
0:001> dd 1ec`e6240000
000001ec`e6240000 00000000 00000000 00000000 00000000
000001ec`e6240010 00000000 00000000 00000000 00000000
000001ec`e6240020 00000000 00000000 00000000 00000000
000001ec`e6240030 00000000 00000000 00000000 00000000
000001ec`e6240040 00000000 00000000 00000000 00000000
000001ec`e6240050 00000000 00000000 00000000 00000000
000001ec`e6240060 00000000 00000000 00000000 00000000
000001ec`e6240070 00000000 00000000 00000000 00000000

Read More

Bypass ACG With UnmapViewOfFile

https://bugs.chromium.org/p/project-zero/issues/detail?id=1435&can=1&q=&start=1200 p0 研究者 ifratric 提出的一种绕过 ACG 保护的方案,即通过调用函数 UnmapViewOfFile 来预先修改内存块内容,从而在可执行页上加入攻击者自定义代码。

Remote JIT

开启了 ACG 保护之后进程将不能够申请未签名代码页。但是现代浏览器为了提高响应效率,都提供了 JIT 功能用于把 JS 代码编译成本地代码。该功能的实现依赖于在进程空间中动态的生成未签名的代码页。为了同时支持 ACG 和 JIT,Chakra 将其 JIT 功能移动到一个单独的进程中,该进程在其独立的沙箱中运行。JIT进程负责将 JavaScript 编译为本地代码并将其映射到请求进程。渲染进程本身不允许直接映射或修改其自己的JIT代码页。

JIT 地址管理

(由于笔者对于 ACG 的分析基于的 Edge 版本较老因此,可能有些地方不匹配)
远程分配的地址空间来源于本进程中的 PreReservedSectionAllocWrapper 对象,该对象在 JIT 进程启动时进行初始化,专门负责管理那些远程保留空间。对象的结构简单表示如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PreReservedSectionAllocWrapper
{
BVStatic<PreReservedAllocationSegmentCount> freeSegments; //0x00 // 用于索引整块内存空间,检查内存状态
LPVOID preReservedStartAddress; //0x200 // 当前应该分配出去的地址
CriticalSection cs; //0x208
|- PRTL_CRITICAL_SECTION_DEBUG DebugInfo; //0x208
|- LONG LockCount; //0x210
|- LONG RecursionCount; //0x214
|- HANDLE OwningThread; //0x218 from the thread's ClientId->UniqueThread
|- HANDLE LockSemaphore; //0x220
|- ULONG_PTR SpinCount //0x228
HANDLE process; //0x230 // 远程进程句柄
HANDLE section; //0x238 // 远程地址 section 句柄
EnsurePreReservedRegionInternal(); // 用于检查是否还存在可管理的 RESERVE 属性内存,若不存在,则在分配之
};

Read More

Visual Studio 注释扩展

在阅读大型工程源代码的时候经常需要根据自己的理解为代码添加某些注释,直接在源码文件中的修改有可能会导致工程重新编译,git 更新代码时产生冲突等情况,更有甚者一旦代码通过 git 更新之后,自己添加的注释将不会出现在新代码中;如果将注释写在源码之外的文件中,在以后查阅和对照时又会有其他新的麻烦。基于以上需求,笔者实现了一个 Visual Studio 中的注释插件,可以在源码文件之外独立添加注释,并且在查看源码时会在对应的位置显示注释。

效果如图所示

在编写过程中参考了这篇文章 中插件的实现。感谢这位作者的分享。

Visual Studio Extension

微软为 vs 扩展的开发者提供了一套完整的开发文档,以便开发者可以根据自己的需求扩展 Visual Studio 的功能。理论上而言可用的扩展点如下

  • 命令和菜单
  • 工具窗口
  • 编辑器窗口
  • 语言服务
  • 项目扩展
  • 用户设置
  • 属性和属性窗口
  • Visual Studio 独立 Shell

其他更多关于 VS 扩展的内容可以参见上面的开发文档,其中提供了大量的例子可以参考。

实现

下面笔者将一步一步记录下整个插件的编写过程,以便之后改进和复现。

Read More