首页
社区
课程
招聘
[原创]时过境迁,当前的DMA变成什么样了?DMA对抗浅析
发表于: 1天前 560

[原创]时过境迁,当前的DMA变成什么样了?DMA对抗浅析

1天前
560

经历了多年的DMA热潮终于落下帷幕,如今DMA作弊已经形成了相当大的产业链,时过境迁,曾经的最强现在到底是不是入国军呢?
正好最近真正在做一些相关探索,于是乎水一篇大粪在这,本人水平低下还望大佬指正。


【1】 出世

本来大家都对抗的好好的你突然用硬件酱味打击这不胡闹吗?galgame里不是这样的,你应该先和我在R3上一决高下最后再在R0打的难舍难分... 相信大家对DMA的工作原理早就有过充分了解所以也不再赘述,非要说就是:


配置空间(Configuration Space)
  CPU 发起 → Root Complex → PCIe TLP(Config) → 设备
  用途:枚举设备、分配 BAR、读写能力寄存器
  特点:请求者是 CPU,不经过 IOMMU
内存访问(DMA)
  设备发起 → PCIe TLP(MemRd/MemWr) → [IOMMU] → 内存控制器 → 物理内存
  用途:设备读写系统内存
  特点:请求者是设备,IOMMU 启用时经其翻译 (后面要考)

是的就这样,使用DMA可以很轻松的做到任意物理内存读取,写入,更重要的是它不会留下痕迹。和很多的技术手段一样,本来DMA是老老实实作研究用的,可是君子无罪怀璧其罪。

总而言之,DMA刚出来的时候确实是完完全全的碾压了市面上所有的反作弊,没有人使用DMA去对抗反作弊,反作弊也就没有对应的需求,自然而然更加的海阔天空,第一批DMA使用者也就切切实实享受到了新世界。


2016年左右,PCILeech发布 (DMA读写从未如此简单):) DMA读写从此工程化

fpga固件        收发 PCIe TLP
  |
leechcore       传输层
  |
vmm/MemProcs    物理内存 → 进程视图、符号解析
  |
上位机程序      读目标数据

19-21年这个时间段就依旧开始出现咋混共作弊的DMA作弊硬件流入市场,配套服务也非常好,固件配套服务也开始出现,逐渐成熟化了,对抗也进入了下一阶段。


【2】大众

总所周知,追求小众是一件非常大众的事情,而在反作弊对抗这块可以说大众了,就再也没有翻身的余地了,技术的泛滥与不断迭代,让旧技术狠狠的被碾压过去,不幸的,很显然DMA也没有逃过时代车轮的碾压,就在某个时间节点,DMA的用户迎来了大爆发,有人赚的盆满钵满,固件,教学等等雨后春笋一般冒出,设备成本的降低和名色大噪又反向助推用户数量的增长,于是肉眼可见的,DMA被盯上了。

另一边,反作弊厂商可以说是亚历山大了,于是正式打响对抗战争。


反作弊厂商首先想到的对抗方法是什么呢?既然DMA是设备那显然他们可以先遍历检查一下:

以下代码为伪代码还原示例 

ANSI_STRING a; UNICODE_STRING u;
char path[] = "\\Driver\\pci";              
RtlInitAnsiString(&a, path);
RtlAnsiStringToUnicodeString(&u, &a, TRUE);
g_pci = ObReferenceObjectByName(&u, ...);   

 //遍历 PCI 总线驱动自己的设备链表
 //g_pci + 0x200 是 LIST_ENTRY 头(node+0 = Flink,node+8 = Blink)
 //记录基址 elem = node - 0x280
 //配置空间头就嵌在 elem + 0x04   
 
for (node = *(PVOID *)(g_pci + 0x200); ; node = *(PVOID *)node) {

    Word vendor = *(Word *)(elem + 0x04);  
    Word device = *(Word *)(elem + 0x06); 

    if (vendor != 0x8086) continue;         // 判断是不是Intel
    if (device <= 0x7D0B) {               
        if (device == 0x7D0B || device == 0x201D || device == 0x28C0 ||
            device == 0x467F || device == 0x4C3D) goto matched;
    } else {
        if (device == 0x9A0B || device == 0xA77F || device == 0xAD0B) goto matched;
    }
    continue;

    if (!(flags & 8)) continue;            

    先拿这块设备内核 PDO,再找到分配的 MMIO 资源
    PDEVICE_OBJECT pdo = *(PDEVICE_OBJECT *)(elem + 0x108);         
    ULONG len = 0;
    status = IoGetDeviceProperty(pdo,
                 0x15 /* DevicePropertyAllocatedResources */,
                 0, NULL, &len);                                    
    if (status != 0xC0000023 /* STATUS_BUFFER_TOO_SMALL */) bail;

    PVOID buf = ExAllocatePoolWithTag(NonPagedPool, 0x200, 'pciR');  
    IoGetDeviceProperty(pdo, 0x15, 0x200, buf, &len);               

    PCM_RESOURCE_LIST rl = buf;
    if (rl->List[0].PartialResourceList.PartialDescriptors[0].Type
            != 3 /* CmResourceTypeMemory */) bail;                  
    bar    = ...[0].u.Memory.Start;                                 
    barLen = ...[0].u.Memory.Length;                                 

    //越过 VMD 桥本身,直读它下游总线的配置空间窗口 
     if (barLen >= 0x100000) {                                       
        p = MmMapIoSpace(bar + 0x100000, 4, MmNonCached);          
        if (*(ULONG *)p == 0xFFFFFFFF) {                             
            //总线 1 上没东西 
            MmUnmapIoSpace(p, 4);
            ExFreePoolWithTag(buf, 0);                              
        } else {
            //上有设备 → 把它的完整 256 字节配置空间读下来 
            MmUnmapIoSpace(p, 4);                                    
            if (barLen >= 0x100000 + 0x1100) {                       
                out = 0;
                sub_14001BAF0(bar + 0x100000, &out, elem);           
            }
        }
        if (barLen >= 0x200000) {                                    
            q = MmMapIoSpace(bar + 0x200000, 4, MmNonCached);        
            if (*(ULONG *)q == 0xFFFFFFFF) { MmUnmapIoSpace(q, 4); }
            else { //同上 }
        }
        ExFreePoolWithTag(buf, 0);                                  
    }
    sub_14001BAF0(bar + 0x100000, &out, elem);                     
}

void sub_14001BAF0(PHYSICAL_ADDRESS addr, ULONG *out, UCHAR *elem)
{
    void *p = MmMapIoSpace(addr, 0x1100, MmNonCached);        // 0x1400d7338
    scratch = ExAllocatePoolWithTag(0x200, 0x1100, 'pcfg');   // 0x1400d743d

    i = 0x1000;
    while (((UCHAR *)p)[i] == 0xFF) i++;                      // 全 0xFF 无设备

    sub_140026280((UCHAR *)p + 1, (UCHAR *)p + 0x1001, 0xFF); // 0x1400d768a

    *out = 1;
    for (i = 0; i < 0x100; i++)                               // 展开 256 条 store8
        elem[0x158 + i] = ((UCHAR *)p)[0x1000 + i];           // → elem[0x158..0x257]

    MmUnmapIoSpace(p, 0x1100);
}


那么这几个case 的十六进制常量是什么呢? 通过查看PCI ID Repository我们可以看到

0x201D 0x467F 0x4C3D 0x9A0B = "Volume Management Device NVMe RAID Controller";
0x28C0 = "Volume Management Device (VMD)";
0x7D0B = "Core Ultra 200H/200V Series Processors VMD";
0xA77F = "RST Volume Management Device Controller";
0xAD0B` = "Core Ultra 200 Series Processors VMD";

那就会有人问了,反作弊拿这个elem 0x158-257又是干什么呢?难道里面有什么可以判断是不是DMA的信息吗。

于是我们就查一查

status = sub_14001C9D0(...);                    // 填充记录(含直读配置空间)
if (status < 0) goto done;

node = *(PVOID *)(P + 0x200);
if (node == (PVOID *)(P + 0x200)) goto empty;   // 空链

out = user_buffer;
out[0x00..0x02]        = elem[0x00..0x02];
*(WORD *)(out + 0x03)  = *(WORD *)(elem + 0x04);      // VendorID
*(WORD *)(out + 0x05)  = *(WORD *)(elem + 0x06);      // DeviceID
*(U64  *)(out + 0x07)  = ((U64)*(ULONG*)(elem+0x120) << 32)
                       |  (U64)*(ULONG*)(elem+0x11C); 
*(ULONG*)(out + 0x0F)  = *(ULONG *)(elem + 0x130);
*(U64  *)(out + 0x13)  = *(U64   *)(elem + 0x128);
*(ULONG*)(out + 0x33)  = *(ULONG *)(elem + 0x14C);
*(ULONG*)(out + 0x37)  = *(ULONG *)(elem + 0x150);
*(ULONG*)(out + 0x3B)  = *(ULONG *)(elem + 0x154);
*(U64  *)(out + 0x3F)  = *(U64   *)(elem + 0x158);    // ← 直读配置空间七点
/* 一直到 out+0x27E */

*(ULONG *)r14 = 2;
sub_14001BD00(P + 0x210, out + 0x27E, ...);

直接查资料看看他都收集了哪些东西吧,忘了说了这么多还没贴出来资料:

typedef struct _PCI_COMMON_HEADER
0x00   Vendor ID   USHORT VendorID / PCI_VENDOR_ID 0x00   
0x02   Device ID   USHORT DeviceID / PCI_DEVICE_ID 0x02   
0x04   Command(16 位)   PCI_ENABLE_IO_SPACE 0x0001 / PCI_ENABLE_MEMORY_SPACE 0x0002 / PCI_ENABLE_BUS_MASTER 0x0004   
0x06   Status   PCI_STATUS_CAPABILITIES_LIST 0x0010 等   
0x08   Revision ID   UCHAR RevisionID   
0x09   ProgIf(编程接口)   UCHAR ProgIf / PCI_CLASS_PROG 0x09   
0x0A   SubClass   UCHAR SubClass   
0x0B   BaseClass   UCHAR BaseClass / PCI_CLASS_DEVICE 0x0a(16 位,含 SubClass)   
0x0E   HeaderType   PCI_HEADER_TYPE 0x0e;0=普通设备,1=PCI 桥,2=CardBus   
0x10–0x27   BAR0–BAR5(6×32 位)   ULONG BaseAddresses[PCI_TYPE0_ADDRESSES],PCI_TYPE0_ADDRESSES 为 6   
0x28   CIS / CardBus 指针   ULONG CIS   
0x2C   Subsystem Vendor ID   USHORT SubVendorID   
0x2E   Subsystem ID   USHORT SubSystemID   
0x30   Expansion ROM 基址   ULONG ROMBaseAddress   
0x34   Capabilities Pointer   UCHAR CapabilitiesPtr / PCI_CAPABILITY_LIST 0x34   
0x3C   Interrupt Line   UCHAR InterruptLine   
0x3D   Interrupt Pin   UCHAR InterruptPin(只读)


0x00   Vendor / Device ID   身份;DMA 卡常报成网卡/声卡   
0x04   Command:bit1 Command寄存器
0x08–0x0B   Revision / ProgIF / SubClass / BaseClass   你是什么设备   
0x10–0x27   BAR0–BAR5   MMIO   
0x2C   Subsystem Vendor / Device      
0x34   Capabilities Pointer   能力链头   
0x3C/0x3D   Interrupt Line / PIN   中断



什么是MMIO?

MMIO就是 Memory-Mapped I/O,0x10–0x27  那 6 个 32 位 BAR 是设备声明自己需要一段地址空间的寄存器

#define PCI_BASE_ADDRESS_0   0x10
#define PCI_BASE_ADDRESS_5   0x24
#define  PCI_BASE_ADDRESS_SPACE        0x01  /* 0 = 内存空间, 1 = I/O 空间 */
#define  PCI_BASE_ADDRESS_MEM_TYPE_64  0x04  /* 64 位地址 */
#define  PCI_BASE_ADDRESS_MEM_PREFETCH 0x08  /* 可预取 */

系统给这段空间分配物理地址后把基址写回 BAR;之后 CPU 访问这段物理地址,就是访问设备的板载寄存器。


为什么收集MMIO?

设备内部的寄存器里,包含 DMA 引擎的描述符地址、长度、启动位、完成状态。这些寄存器通常映射在 BAR 里,CPU 只能通过这段 MMIO 去操作 DMA 。



为什么收集 Command 寄存器?

#define PCI_ENABLE_IO_SPACE                 0x0001
#define PCI_ENABLE_MEMORY_SPACE             0x0002   // bit1
#define PCI_ENABLE_BUS_MASTER               0x0004   // bit2 
#define PCI_ENABLE_SPECIAL_CYCLES           0x0008
#define PCI_ENABLE_WRITE_AND_INVALIDATE     0x0010
#define PCI_ENABLE_VGA_COMPATIBLE_PALETTE   0x0020
#define PCI_ENABLE_PARITY                   0x0040   // (ro+)
#define PCI_ENABLE_WAIT_CYCLE               0x0080   // (ro+)
#define PCI_ENABLE_SERR                     0x0100   // (ro+)
#define PCI_ENABLE_FAST_BACK_TO_BACK        0x0200   // (ro)
#define PCI_DISABLE_LEVEL_INTERRUPT         0x0400

当 PCI_ENABLE_BUS_MASTER = 1 时,该 Function 可以主动向总线发起 Memory Read / Write Request(即 DMA 操作)。

当 PCI_ENABLE_BUS_MASTER = 0 时,该 Function 不允许发起任何 DMA 请求,只能被动响应 CPU 的访问。




还可以在用户态继续一下

HDEVINFO h = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
for (DWORD i = 0; SetupDiEnumDeviceInfo(h, i, &di); i++) {
    SetupDiGetDeviceRegistryProperty(h, &di, SPDRP_HARDWAREID,
                                     NULL, buf, sizeof buf, NULL);
                                     
   //读取Vendor ID对比一下黑名单id
}

这里也有一些被拉黑的数据,收集数据,建立数据库狠狠的拉黑这些板子使用者。

109E   Brooktree Corporation   Bt848 / Bt878 / Bt879 视频采集   pci-ids 109E   
1CD7   Nanjing Magewell Electronics(美乐威)   Pro Capture / Eco Capture 采集卡   pci-ids 1CD7   
BDBD   Blackmagic Design   DeckLink / Intensity / UltraStudio 采集卡   pci-ids BDBD (注意都是采集卡下面也要考)


校验方案:

//没服务
for (int i = 0; i < total; i++, elem += 0x848) {
    if (*(U32 *)(elem + 0x808) == 0)          //设备没有绑定任何驱动服务
        //上报
}
//有服务
for (int i = 0; i < total; i++, elem += 0x848) {
    if (*(U32 *)(elem + 0x808) == 0)  continue;    // 无服务 → 归 dma_nosrv 管
    if (*(U32 *)(elem + 0x812) != 786) continue;   // ← 第二个判据
    if (ven_match(o, hwid(elem), o->vendor_list))
        report_event(7990, 102, 9015);
}
//假服务:
bool vm = ven_match(o, hwid(elem), o->vendor_list);   // 子串查找
bool vs = svc_match(o, svc(elem),  o->service_list);  // 字符串全等
if (vm && vs) report_event(7990, 102, 9019);


bool ven_match(Type134 *o, char *hwid, vector<string> *list) {
    if (list->empty() || !hwid) return false;
    for (each v in *list) {
        string pat = "VEN_" + v;                 
        if (sub_181737594(hwid, pat.c_str()))    // ← 在 hwid 里找这个子串
            return true;
    }
    return false;
}

if (*(U32 *)(elem + 0x00) == 0 &&
    *(U32 *)(elem + 0x04) == 0 &&
    *(U32 *)(elem + 0x08) != 0 &&              // ← 这一项要求"非零"
    *(U32 *)(elem + 0x10) == 0 &&
    *(U32 *)(elem + 0x14) != 0 &&
    all_zero(elem + 0x18, 256) &&              // ← 连续 256 字节全零
    total == 1)                                // ← 记录总数恰好为 1


这里给出参考表也不再赘述了,核心对抗就是你伪造的我就校验,这也是为什么某些垃圾固件确实会拉闸。

elem+0x808   U32   nosrv / wrongsrv   == 0   无驱动服务   
elem+0x812   U32   wrongsrv   == 786   服务起不来   
硬件 ID 字符串   string   fakesrv   VEN_<厂商> 子串   厂商身份   
服务名字符串   string   fakesrv   全等 3 个虚拟机名   伪造服务名   
elem+0x00/04/08/10/14   5×U32   hide   零/非零交替   空壳形态   
elem+0x18..0x117   256 B   hide   全零   该填未填   
记录总数   int   hide   ==1 / 变化   藏得太干净   
两来源 ID   U32×2   cfg   互相相等   读取链被改


【3】现在

难道说DMA不小众了我就不玩了吗,但那是弱者的思维。哪怕是在VTD大清洗后,还是有相当数量的八旗子弟仍旧抱着DMA四死死不放手,这也能理解,毕竟双机硬件搭配依旧能解决很多问题,比如签名,软件特征等等。

DMA为了应对反作弊厂商的严肃进攻,也是做出了努力,比如他们尝试伪装固件,把收集到的公开的固件刷到自己这里,或者伪装自己的ID用于对抗反作弊的黑固件数据库,甚至是自己实现一个协议去对抗反作弊的行为检测。


不过反作弊也迎来了史上最强VTD vs 现代最强DMA

0xe87f  test al, al
0xe881  je   0x14000e890
0xe883  cmp  byte ptr [0x14003027A], 0        ; g_securePciEnabled
0xe88a  jne  0x14000e912                      ; Secure PCI 已启用 → 跳过整段
0xe890  cmp  dword ptr [rbp - 0x7c], 0x10ec   ; 未启用 → 检查 vendor == Realtek
0xe897  jne  0x14000e912
0xe8a5  lea  rdx, [r13 + 0x10]                ; 遍历设备条目,步长 0x14
0xe8c5  cmp  byte ptr [rcx], 3                ; 子类型 == 3
/* g_securePciEnabled 由 VslGetSecurePciEnabled() 写入(0x14000D586 → 0x140016F5C) */
if (secure_pci_enabled)
    return;                                   //直接过
if (vendor != 0x10EC)
    return;
    
/* 遍历设备条目,按子类型继续查 */

系统已经启用了 Secure PCI就不管,至于为什么只检查Realtak就懂的都懂了。



不过反作弊也利用起了大杀器,VTD,还记得前面说的IOMMU吗?这里就考到了,DMA要访问是必须经过IOMMU的。

在没有VTD时候,DMA是这样访问的:

设备 ──DMA──▶ 物理地址 0x0000_0000 / 内核代码 / 其他进程 / 其他 VM

而在打开了VTD之后就是如此访问的:

PCIe 设备
   │ DMA 请求(带 IOVA + Requester ID / BDF)
   ▼
VT-d DMA Remapping Engine(Root Complex 里)
   │ 1. 用 BDF 查 Root Entry → Context Entry
   │ 2. 找到这个设备的 I/O 页表(Domain)
   │ 3. IOVA → HPA 翻译
   │ 4. 检查 R/W/EXEC 权限
   ▼
物理内存



0x00 Signature      "DMAR"
0x04 Length
0x08 Revision
0x09 Checksum
0x0A OEMID          6 字节
0x10 OEM Table ID   8 字节
0x18 OEM Revision
0x1C Creator ID     4 字节   
0x20 Creator Revision
0x24 HAW
0x25 Flags
0x26 Reserved
0x28 Remapping Structures (DRHD/RMRR/ATSR/...)


PCIE设备只能看到IOVA(I/O VirtualAddress),而不是直接拿到真实的物理地址了,此时此刻真实的物理地址由VTD硬件查表页决定。

而如果他要读取就需要系统在IOMMU页表里建映射表,如果没有映射的话就会计入DMAR fault,看到这里你应该也能感觉到VTD的姜维打击了,还是那句话,如果身份互换,我会让你知道我的痛苦,可以说,打开VTD几乎DMA就没有还手之力了。


0x140016F5C  mov  rax, [0x140032550]      ; g_VslGetSecurePciEnabled
0x140016F63  test rax, rax
0x140016F66  je   0x140016F6B
0x140016F68  jmp  rax
0x140016F6B  ret                          ; 指针为 NULL → al = 0 → 返回 FALSE

当然DMA玩家也并非全无办法,比如:想办法解析DMAR表篡改数据,或者伪造设备已经打开了VTD。

比如我可以构造一张假的DMAR表:

定位 RSDP → 找到 XSDT 物理地址
解析 XSDT → 收集所有 ACPI 表项地址
扫描物理内存,找 4KB 对齐且空闲的区域(与现有表至少隔 4KB)
在该区域构造一份合法的 DMAR 表(含 DRHD/RMRR 等子结构)
DRHD 寄存器基址选 >4GB 的空闲 PCIe MMIO 页
把新 DMAR 的物理地址写入 XSDT 条目(替换或追加)
修正校验和,回读验证
观察系统 IOMMU 行为变化



关键代码: 取自kEv1nZ0/VTD-Bypass: 全自动过VTD检测

定位 RSDP → XSDT
# 扫描物理内存低 1MB 找 RSDP
for pa in range(0x000E0000, 0x00100000, 16):
    buf = lc.read(pa, 36)
    if buf[0:8] == b'RSD PTR ':
        rsdp = buf
        break

xsdt_pa = struct.unpack_from('<Q', rsdp, 24)[0]  # XsdtAddress at offset 24


找空闲 4KB 对齐区域

def find_free_4k(start, end, used_list):
    """找和 used_list 中每个地址至少隔 4KB 的空洞"""
    for pa in range(start, end, 0x1000):
        conflict = False
        for used in used_list:
            if abs(pa - used) < 0x1000:
                conflict = True
                break
        if not conflict:
            return pa
    return 0
    
构造 DMAR 表
def build_dmar(dmar_pa):
    dmar = bytearray(48)  # 表头 48 字节
    struct.pack_into('4s', dmar, 0, b'DMAR')
    struct.pack_into('<I', dmar, 4, 48)
    dmar[8] = 1           # Revision
    # OEMID / OEM Table ID / Creator ID 模仿原固件
    # Checksum 后面算
    return dmar

def calc_checksum(buf):
    return (-sum(buf)) & 0xFF

dmar = build_dmar(dmar_pa)
dmar[9] = calc_checksum(dmar)


DRHD 基址选 >4GB MMIO
def pick_drhd_base():
    """选 >4GB 的空闲 PCIe MMIO 页"""
    for pa in range(0x100000000, 0x10000000000, 0x1000):
        if not is_mmio_mapped(pa) and not is_used(pa):
            return pa
    return 0
    

写入新 DMAR 并挂进 XSDT
# 写入 DMAR 表体
lc.write(dmar_pa, bytes(dmar))

# 替换 XSDT 中的 DMAR 条目
for i, entry in enumerate(entries):
    if lc.read(entry, 4) == b'DMAR':
        lc.write(xsdt_pa + 36 + i*8, struct.pack('<Q', dmar_pa))
        break

# 修正 XSDT 校验和
xsdt_data = lc.read(xsdt_pa, length)
xsdt_data[9] = calc_checksum(xsdt_data)
lc.write(xsdt_pa, xsdt_data)


最后再回读验证一下
verify = lc.read(dmar_pa, 4)
assert verify == b'DMAR', "DMAR 写入失败"

verify_xsdt = lc.read(xsdt_pa + 36 + i*8, 8)
assert struct.unpack('<Q', verify_xsdt)[0] == dmar_pa, "XSDT 条目更新失败"

当然,大部分反作弊也会在用API判断完后检查DMAR表,也可以利用固件元数据不一致来判断环境是否可信。

*(U32*)(dmar+0x0B)   0x0B   OEMID(0x0A,6) 之内     
*(U16*)(dmar+0x0F)   0x0F   OEMID 末 + OEMTableID 首   
*(U32*)(dmar+0x1D)   0x1D   CreatorID(0x1C) 之内   
RtlCompareMemory(dmar+0x0B, sdt+0x0B, 6)   0x0B 起 6 B 

伪代码:
PVOID dmar = FindAcpiTable('DMAR');
PVOID root = FindAcpiTable('XSDT');  

bool vt_d_seen = (dmar != NULL);                                     
bool same_oem  = vt_d_seen && RtlCompareMemory(dmar + 0x0B,
                                   root + 0x0B, 6) == 0;



【最后】

感谢@Peyria 的支持,感谢各位能读到这,也衷心希望大佬们指出错误(排版真的好麻烦)



传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

最后于 1天前 被n013ody编辑 ,原因:
收藏
点赞 ・3
打赏
分享
最新回复 (4)
雪    币: 294
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2 楼
真就是道高一尺魔高一丈,一直对抗
22小时前
0
雪    币: 6572
活跃值: (8649)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3 楼
预测未来的作弊方式,国产光刻机技术溢出到下沉市场,个人代工cpu更容易更廉价后。作弊者开始自己设计cpu架构,并完全遵循了intel的手册规范。能跑x86指令,从此新的战争又开始打响了。
21小时前
0
雪    币: 6572
活跃值: (8649)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4 楼
关于DMA作弊的帖子在UC上搜索,最早可以找到2001年UC上就有人在讨论用DMA进行作弊,直到2016年PCILeech这个开源项目才问世。
21小时前
0
雪    币: 270
活跃值: (711)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5 楼
真正成熟的表现是对所有人,所有事去魅
18小时前
0
游客
登录 | 注册 方可回帖
返回