昨日,随着微博大v @夏雪宜BTC 的曝光,一种新型骗局进入大家的视线,一时间币圈人人自危:
原微博如下:
微博原文
大概是:只要你通过二维码给骗子转过一次 usdt,之后你账户的 usdt 就会被骗子转光。看起来非常正常的二维码交易,为什么骗子在转账一次之后就能控制用户的钱包呢?
下面对犯罪手法做简要分析。
扫描这个二维码:
会访问以下链接:
https://huobi4.gzimtoken.com/trc20.html?s=axu4&addr=TXpSZHhde2UsqQLZMsh14DeKaS1fdRrTuL
点开之后是这样的:
使用 TokenPocket 钱包扫码,尝试转账,注意一定是波场链,下图注意左上角:
会发现过程中会有如下请求。请注意方法:approve
!
或者使用 tronlonk 查看:
这个合约的形式是 approve,上面这个图的意思是请求批准转账 90000000 usdt!
而此处的请求签名本应该是授权,而 Tronscan 把授权识别成了转账。
现在大家应该明白如果点了接收的后果,approve 授权你就把自己的 usdt 钱包授权给了攻击者的地址,90000000 之内的 usdt 可以被攻击者转走。
这种手法也被称之为 “approve钓鱼” 。
请求签名的详情为:
从弹出的授权里,看得到是授权给 TVM7gSoNdpgup9SbTKWWY2dHhLVjhehGiy 这个地址,然后去查此地址的转账记录,可以看到 TRC20&TRC721 转入记录全部显示90000000:
这些转入账户就全都是受害者。这其实是 tronscan 的 bug,90000000 的只是授权,还不是转账。
比如随便看一笔显示金额为 90000000 的交易:
https://tronscan.org/#/transaction/2c08ccf5c8fcbeab2e994d57a442a5529d295aec7115db131f24256c5db9809a
可以看到授权:
也仅仅是授权,这里提示小白,
根据 https://www.freebuf.com/articles/web/231663.html 一文中展示的 shellcode 生成框架、实现了基于 Win32 API 的远程文件下载功能。
项目代码位于:
https://github.com/Snowming04/shellcode_generate-test_framework
因为此文中原理论述足够清楚,故本文中不再谈论。
本次工程创建是win32空项目,使用的是VS2019编译relase/x86,编译之前进行如下设置:
头文件:
源代码文件:
其他文件:
本文希望通过使用 Windbg 调试 kernel32.dll,查看其导出表、 探索 IMAGE_EXPORT_DIRECTORY
数据结构,理解以下三个表之间的相互关系:
便于更好的理解后续通过函数名称在内存中获取函数指针的 shellcode 编程。
将 WinDbg attach 到 C:\Windows\System32\notepad.exe
(x64) 程序上。
0:000> !peb
PEB at 00000093ec76a000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes
ImageBaseAddress: 00007ff626d20000
NtGlobalFlag: 70
NtGlobalFlag2: 0
Ldr 00007fff777053c0
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 000001986c942ef0 . 000001986c943770
Ldr.InLoadOrderModuleList: 000001986c9430a0 . 000001986c94a760
Ldr.InMemoryOrderModuleList: 000001986c9430b0 . 000001986c94a770
Base TimeStamp Module
7ff626d20000 5e82e461 Mar 31
本文希望通过 shellcode 编程获取 kernel32.dll 的内存地址。获取 kernel32 的内存地址是通过 LoadLibraryA -> GetProcAddress 调用链进行 shellcode win32 编程的基本步骤。
首先通过 windows c 代码获取 kernel32.dll 的内存地址,用于后续的结果验证。
注:编译环境为 win32/x86。
已知 peb 中加载的第三个模块是 kernel32.dll。所以 shellcode 作为一段 PIC (位置无关)代码,我们是希望从 peb 中获取 kernel32.dll 的内存地址。
将 WinDbg attach 到 C:\Windows\SysWOW64\notepad.exe
(x86) 程序上。
将 PEB
数据结构覆盖在所指向的内存上,以查看该结构成员,在此重点关注 Ldr
:
Ldr
是一个 _PEB_LDR_DATA
类型的成员,包含有关当前进程加载模块的信息。
看到偏移 +0x014 处的成员 InMemoryOrderModuleList
。这是一个 _LIST_ENTRY
类型的成员。
可以看到这其实是一个双向链表的指针,其有两个成员变量:
我们都知道,链表的一个结点,既包含了指针信息,也包含了数据。
将此地址覆盖到 LDR_DATA_TABLE_ENTRY
结构查看其数据:
可以看到其 Flink(前一个结点)的 FullDllName 为 notepad.exe
。但是要注意这是头节点,要获得当前节点的 FullDllName 需要 -8(一个结点长度8字节,因为双向链表结点两个指针 4 bytes *2):
可以看到为当前 exe,即为第1个模块。而我们的目的是查看第3个模块并获取其内存地址。
重新回到 InMemoryOrderModuleLis
MSVCRT.lib(exe_main.obj) : error LNK2001: 无法解析的外部符号 main
遇到这个问题的排错思路是:
属性
→配置属性
→C/C++
→预处理器
→预处理器定义
中的 _CONSOLE
。以及属性
→配置属性
→链接器
→系统
→子系统
中的 控制台(/SUBSYSTEM:CONSOLE)
,而不要错选成 WINDOWS
。但在这两个前提下,依然报这个错误。那么就要考虑到是否真的编译出 main 函数了。
原来是因为最开始在添加包含 main 函数的源代码文件时候,错选成了 .h 头文件,后面虽然改成了 .cpp 文件,但实质上在解决方案中此文件并未参与编译。于是把此文件从项目中排除
然后重新添加现有项就可以了。
注意要点
最后稍微列一下C++包含头文件的顺序,同样来源于上一个引用链接。
要注意的是一些头文件也有依赖关 系,这些文件的包含顺序也小心,否则就会出错。ps,头文件的包含顺序应该是从最特殊到一般,比如:我们应该以这样的方式来#include头文件:
从最特殊到最一般,也就是:
#include "本类头文件"
#include "本目录头文件"
#include "自己写的工具头文件"
#include "第三方头文件"
#include "平台相关头文件"
#include "C++库头文件"
#include "C库头文件"
本来我在做使用 WIN32 API 加密的时候(比如 AES、RC4) 是通过 CSP 实现的。CSP 是一个独立的软件模块,实际上执行用于身份验证,编码和加密的密码算法。CSP 提供了一组密码学 API,使应用程序开发人员可以向基于 Windows 的应用程序添加身份验证,编码和加密。
但是现在有了新一代密码学 API,也被称为 CNG(Cryprography API: Next Generation
),MSDN 号称:
之前的
Cryptography API
不再推荐使用。
如:
新的和现有的软件应开始使用Cryptography Next Generation API。Microsoft可能会在将来的版本中删除之前的
Cryptography API
。
本文就是使用 CNG(新一代密码学 API)来示范 AES 的使用,因为避免关联,不会给出完整代码,使用的尽量是 MSDN 上的代码或来源于其他合法渠道的公开代码示例。
CBC 模式是需要使用 IV 的。IV 即初始化向量,相比于 ECB 模式,CBC 模式对每个明文分组要与前一个密文分组进行异或,这样的作用是即时两个明文分组的值是相等的,经过多字节异或
,对应的两个密文分组的值也不一定是相等的。而IV 的使用就是为了解决第一个明文分组跟谁异或的问题。
当加密第一个明文分组时,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来代替“前一个密文分组”,这个比特序列称为初始化向量(Initialization Vector),通常缩写为IV,一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
这里要注意,IV 的长度与分组的大小是一致的。而 AES 的标准分组为 128 Bits,即为 16 字节。
AES 有三个输入和一个输出:
CS (测试版本 4.0)生成的默认宏样本,如下定义此结构体:
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
但其实:
hProcess As Long
hThread As Long
在64位机器环境下均定义错误。
因为 64 位环境下, VBA 中 Long 仅为4字节,如果按照这个宏样本去运行,取第三个参数的值,就会发现并不是正确的 PID:
Debug.Print pInfo.dwProcessId
但 CS 的默认宏样本却能正确运行,这是因为 hProcess 不过是一个进程句柄,是可以被4字节容纳下的。但是错误的内存大小定义会影响到占位和第三个参数的值,所以应该根据系统选择性的定义:
#If VBA7 And Win64 Then
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
#Else
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
#End If
注: LongPtr
在64位系统中为8字节。
什么是纤程(Fibers)?
用一个类比:线程是进程内部的实体,那么纤程则又是纤程内部的实体。
在《Windows Internals(第六版)》一书中提到:
因为将 CPU 的执行从一个线程切换到另一个线程,将不可避免地涉及内核调度器,所以,这可能是一个开销昂贵的操作,如果两个线程经常频繁地来回切换则尤其如此。 Windows 实现了两种机制来降低这一开销:纤程(fiber)和用户模式调度( UMS , user-mode scheduling )。
纤程使得一个应用程序可以调度它自己的“线程”的执行过程,而不必依赖于 Windows 内置的基于优先级的调度机制。纤程也常被称为“轻量”线程:从调度的角度来看,它们对于内核是不可见的,因为它们是在用户模式下在 Kemel32.dll 中实现的。为了使用纤程,首先要调用 Windows 的 ConvertThreadToFiber 函数。该函数将当前线程转变成一个正在运行的纤程。之后,在转变得到的纤程中,通过调用 CreateFiber 函数,又可以创建额外的纤程(每个纤程可以有它自己的一组纤程)。然而,与线程不同的是,纤程不会自动执行,它必须由 SwitchToFiber 函数手工选中,然后才能执行。新的纤程会一直运行,直到退出,或者调用SwitchToFiber再次选择运行另一个纤程。
总结一下,纤程是执行单元,其必须由应用程序进行手动调度。纤程在对其进行调度的线程的上下文中运行。
本文中,企图通过利用和纤程相关的 Win32 API,来在本地进程中执行 shellcode。绕过一些防御设备对常见进程注入 API 的拦截。
为了使用纤程执行 Shellcode,调用链为:
ConvertThreadToFiber
函数。VirtualAlloc
+ memcpy
函数。CreateFiber
函