Aggregator
Submit #578254: PHPGurukul Daily Expense Tracker System 1.1 SQL Injection [Accepted]
Submit #578194: PHPGurukul Daily Expense Tracker V1.1 SQL Injection [Accepted]
CVE-2025-48187 | infiniflow ragflow up to 0.18.1 excessive authentication (EUVD-2025-15586)
Submit #578143: PHPGurukul Park Ticketing Management System 2.0 SQL Injection [Duplicate]
Submit #578142: PHPGurukul Park Ticketing Management System 2.0 SQL Injection [Duplicate]
Submit #578141: PHPGurukul Park Ticketing Management System 2.0 SQL Injection [Duplicate]
Submit #578139: PHPGurukul Park Ticketing Management System 2.0 SQL Injection [Duplicate]
Submit #578138: PHPGurukul Park Ticketing Management System 2.0 SQL Injection [Duplicate]
Submit #578137: PHPGurukul Park Ticketing Management System 2.0 SQL Injection [Duplicate]
IGS Arcade 逆向系列(一)- E2000平台分析
2010年是街机的黄金时代,随着移动终端和家用游戏机普及,街机行业也逐渐走向没落,尽管国内出台了一些政策鼓励游戏游艺设备行业发展,但此行业在未来一直都不被资本看好。2020 疫情更是给电玩行业带来了很大的打击。
在以前,100元可能只能买50个币,现在100元可以买200个币。
我喜欢赛车游戏,湾岸Midnight、头文字D、Speed Driver是近几年电玩城最火的游戏,因为它有账号功能,具备社交属性。前段时间在破解极速5的玩家APP,才知道IGS (鈊象电子)就是我小时候玩的三国战记和西游的厂商,IGS反破解在整个行业都是顶尖的,竞速游戏的极速系列更是号称无人可破解。
上个月,我家旁边的大玩家电玩城倒闭了,我感觉迟早有一天极速系列游戏会消失,便想挑战破解极速系列游戏。
IGS 极速系列发布顺序如下:
Game Device Model Year Speed Driver:Evolution Evolution 2004 Speed Driver 2 E2000 2007 极速 3 E3000, E3100 2010 极速 4 E3000, E3100, S3000 2013 极速 5 S3000 2019我计划从E2000平台开始破解,难度肯定比PGM(PolyGame Master)更低,因为是基于PC开发的,不需要额外模拟声卡和显卡。硬件几乎没有物理保护,CPU指令集是x86,OS是Linux,没有反调试,也没有VMP,这不就是新手村吗。
Speed Driver系列在亚洲影响力媲美湾岸Midnight,头文字D,雷动G(都被破解了)。IGS的反破解是最成功的,在整个产品生命周期内都未被破解。
硬件分析在 Arcade-docs 可以查询到每个设备的硬件信息和支持的游戏 凭借我擅长的淘电子垃圾能力,已经搞到一台E2000主机。国外有不少爱好者购买,所以价格涨上去了。
外部接口分析正面有两个RS-485接口,并且CF卡可以从外部拆卸
背面接口,具有千禧年PC的接口特点
- 12V DC
- 2 x DB9 COM
- 4 x USB 2.0
- RJ45 LAN
- 3.5mm Audio Out
- 25pin + 30pin 的I/O口
考虑到这个破解难度不高,其他贴了贴纸遮盖丝印的元件就不分析了,太麻烦
我买的这个主机安装了 Percussion Master 2008 游戏,但极速2也能运行
- 主板型号:I-JOIN E2000-V256 IH-02 (研华)
- A - CPU:Celeron M 370 (1.5 GHz)
- B - 北桥:未知
- C - GPU:NVIDIA GeForce 6200 (256 MB, GDDR2)
- D - 2 x DDR 333 256MB
- E - Chipset: Intel 852GME (ICH4-M)
- F - PCI9030,是GPIO芯片,可能用于游戏控制器的信号传输。
- G - I/O 控制器 有LPC接口
- H - A11 BIOS芯片 SST 49LF004B(PLCC32)
- I - IH-C02 ALTERA EPM3032ALC44-10N CPLD (PLCC44), 控制器,里面有ROM,用途不详
- J - IGS EV29LV640-90PCR 8MB EEPROM,DIP 48-Pin 封装,IGS定制的芯片。这个芯片专门贴了游戏名称的标签,说明BIOS ROM 也和游戏有关。可能会有跟V21芯片相关的内容。
- K - V21,IGS036E,也许是一颗FPGA或者ASIC,用于加密处理控制信号的输入输出
- L - 64K x 16 HIGH-SPEED CMOS STATIC RAM x3
- M - CF卡:ADATA 2GB (266X)
- N - IDE硬盘接口
我有很多种办法拿硬件的root shell,但我对游戏加载的过程更感兴趣,因此先做软件逆向分析吧。
I/O板分析这几块板子又称控制板,应该是用于连接游戏控制器,比如方向盘、刹车油门、投币器之类的。
通过连接主板的25pin + 30pin 的I/O口 进行通信
价格可能比主机本体还贵
应该可以使用逻辑分析仪来捕获这些传感器信号,然后实现控制功能。但据说控制信号由V21芯片(ASIC)处理,可能会加密。我也不打算去仿制一个控制板。这种街机游戏没有地平线5好玩。现在电玩城也便宜,真想玩的话,就直接花钱去电玩城,或者买一套方向盘在家里玩。
文件系统分析直接dump CF卡,文件大小2GB,file命令的回显如下:
DOS/MBR boot sector, LInux i386 boot LOader; partition 1 : ID=0x1, active, start-CHS (0x0,1,1), end-CHS (0x2,63,63), startsector 63, 12033 sectors; partition 2 : ID=0x83, start-CHS (0x3,0,1), end-CHS (0x22,63,63), startsector 12096, 129024 sectors; partition 3 : ID=0x83, start-CHS (0x62,0,1), end-CHS (0x399,63,63), startsector 395136, 3322368 sectors; partition 4 : ID=0x83, start-CHS (0x23,0,1), end-CHS (0x61,63,63), startsector 141120, 254016 sectors启动过程,LILO作为MBR,直接安装在第一个扇区,用来引导Linux
+----------------------------------------+ | Master Boot Record Operating system | |----------------------------------------| | LILO ---------------> Linux | | ---> other OS | +----------------------------------------+通过fdisk查看分区信息。是Linux操作系统,四个分区,并首尾连续,最后一个分区之后的内容,都是0x00,不存在隐藏分区
Disk ./percussion_master_2008.img: 1.77 GiB, 1903878144 bytes, 3718512 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x6e4a3fef Device Boot Start End Sectors Size Id Type ./percussion_master_2008.img1 * 63 12095 12033 5.9M 1 FAT12 ./percussion_master_2008.img2 12096 141119 129024 63M 83 Linux ./percussion_master_2008.img3 395136 3717503 3322368 1.6G 83 Linux ./percussion_master_2008.img4 141120 395135 254016 124M 83 LinuxE2000平台的分区有一个特点,第三分区和第四分区,顺序是反的。
第二分区和第四分区,7z无法完全提取,binwalk能识别出一些压缩区域,因此大概率是经过了魔改的文件系统。
每个分区的用途如下(最后我找到了分区2,4的原始文件系统类型):
Partition 1: Ext2, Bootloader, kernel Partition 2: IGS CRAMFS, RootFS Partition 3: Ext3, Log Data Partition 4: IGS SquashFS, Game Data Kernel 逆向目前未找到rootfs和游戏程序,他们大概率位于第二分区和第四分区。 我认为逆向kernel可以少走不少弯路。不依赖内核,其实可以直接硬逆出文件系统的格式,但这样很无聊,而且最终写出来的extractor,肯定比不了开源的文件系统解压工具。 首先看第一个分区,有一个 loader 和 kernel。显示了内核版本,但是这里不一定是正确的版本。
SD2: LILO (LInux LOder) SD2-OS-61P: Linux kernel x86 boot executable, bzImage, version 2.4.31-IGS_V0.5a (james@Code_Server.linnet.net.tw) #1 Thu Aug 30 19:06:24 CST 2007, RO-rootFS, root_dev 0X305, Normal VGA, setup size 512*6, syssize 0x3b88, jump 0x230 0xe800040000000000 instruction, protocol 2.3LILO经过了IGS 定制开发,会显示,IGS Loader v2.0 Boot Menu 2007/04,可判断版本是lilo-22.8
这里没什么好分析的,直接开始提取vmlinux,内核文件是bzImage格式,需要解压
7z x ./SD2-OS-61P -okernel.decomped接下来就是普通的内核逆向,第一步计算基址,基址有很多种确定方法,可以参考我其他文章。
这里多看两眼就猜出来了,0xC0100000
基址配置正确,IDA就可以自动识别出一些交叉引用,剩下的需要手动还原。IDA 9.0修改了一些API,又得重新写一下脚本。
import ida_bytes import ida_segment import ida_funcs import ida_ida def find_and_make_function(start_ea, end_ea, pattern_str): image_base = idaapi.get_imagebase() pattern = ida_bytes.compiled_binpat_vec_t() err = ida_bytes.parse_binpat_str(pattern, image_base, pattern_str, 16) if err: return current_ea = start_ea found_count = 0 while current_ea < end_ea: current_ea, index = ida_bytes.bin_search( current_ea, end_ea, pattern, ida_bytes.BIN_SEARCH_FORWARD | ida_bytes.BIN_SEARCH_NOSHOW) if current_ea == ida_idaapi.BADADDR: break if current_ea % 4 != 0: current_ea += 1 continue if ida_bytes.get_flags(current_ea) & ida_bytes.FF_CODE: if idc.get_func_flags(current_ea) != -1: current_ea += 1 continue seg = ida_segment.getseg(current_ea) if not seg: current_ea += 1 continue if not ida_funcs.add_func(current_ea): print("Create function failed at 0x{:X}".format(current_ea)) else: found_count += 1 current_ea += 4 print("Search finished, {} functions created".format(found_count)) find_and_make_function(0xC0100000, 0xC0508000, "55 89 E5") # 可以根据实际情况自己定义 function entry opcode当然到这里还没有完全识别出全部交叉引用,在data段,还有一些字符串和offset还原的不是很好,但影响不大,实在是没有思路的话,就把这些内容还原,说不定会有新发现。
接下来就是找分析切入点,实在没啥技术含量,我有很多种方案,比如从文件系统特征入手之类的,但全写出来就像说“茴字有五种写法”一样搞笑。
可以确认内核版本是 2.4.31,并且IGS版本是v1.0,而不是bzImage显示的v0.5。
根据下列字符串,可以确定IGS魔改文件系统对应的原始文件系统版本
- rofs:cramfs
- shfs: squashfs 2.2
Linux 2.4.31 没有squashfs,直接找squashfs的patch,IGS魔改SHFS是基于squashfs2.2。 https://master.dl.sourceforge.net/project/squashfs/OldFiles/squashfs2.2r2.tar.gz
Linux 2.4没有支持kallsysm,因此需要手动还原符号。一些printf,memcpy,str之类的函数可以快速一眼看出来,但是我要分析系统启动过程,所以需要还原一些内核专用的符号。
这种老设备,在新的linux下没有办法交叉编译,我安装了一个CentOS 5.10的32位虚拟机,成功编译,提取出 System.map 和 vmlinux,这样就可以方便对比IGS魔改的部分。
对于这种找不到ramdisk或者rootfs的固件,第一步肯定是找到kernel里的sys_mount和boot parameters
root=/dev/hdc2 ro console=ttyS1,115200 sys_mount("/dev/hdc3", "/PM2008v2", "shfs", flag, data) sys_mount("/dev/hdc4", "/PM2008v2/pm2_data", "ext3", flag, data)这里可以确定,rootfs应该是第二分区,游戏数据在第三分区。
挂在ramdisk的逻辑,位于此处
prepare_namespace ->rd_load_disk ->rd_load_image ->identify_ramdisk_image在此次可以确定IGS 支持 squashfs 和 igs rofs 格式的ramdisk。并且可以确定它们的magic。
分析过程就不记录了,他们把 FS 的header魔改了,分析起来有一些恶心🤢。
提取文件系统我分析了 E2000 平台的三种自定义文件系统 header,内容如下。
IGS SHFS V1 Header struct squashfs_super_block_22_v1 { unsigned int s_magic; // 0xD4AA2682 unsigned int block_size_1:16; unsigned int block_log:16; unsigned int major_number; unsigned int minor_number; unsigned int inodes; unsigned int bytes_used; unsigned int uid_start; unsigned int guid_start; unsigned int inode_table_start; unsigned int directory_table_start; unsigned int flags:8; unsigned int no_uids:8; unsigned int no_guids:8; int mkfs_time /* time of filesystem creation */; squashfs_inode root_inode; unsigned int block_size; unsigned int fragments; unsigned int fragment_table_start; } __attribute__ ((packed)); IGS SHFS V2 Header以 Percussion Master 2008 为例
struct squashfs_super_block_22_v2 { unsigned int s_magic; // 0xD4AA2682 char igs_info[64]; // banner unsigned int block_size_1:16; unsigned int block_log:16; unsigned int igs_fs_version_major; // 59 17 23 96 unsigned int igs_fs_version_minor; // E1 5D 70 00 unsigned int major_number; // 31 64 52 E5 unsigned int minor_number; // 92 2C 03 68 unsigned int inodes; unsigned int bytes_used; unsigned int uid_start; unsigned int guid_start; unsigned int inode_table_start; unsigned int directory_table_start; unsigned int flags:8; unsigned int no_uids:8; unsigned int no_guids:8; int mkfs_time /* time of filesystem creation */; squashfs_inode root_inode; unsigned int block_size; unsigned int fragments; unsigned int fragment_table_start; } __attribute__ ((packed)); IGS ROFS V1 Header struct cramfs_inode { unsigned int inode_magic; unsigned int namelen:6, offset:26; unsigned int size:24, gid:8; unsigned int mode:16, uid:16; }; struct cramfs_info { unsigned int crc; unsigned int edition; unsigned int blocks; unsigned int files; }; struct igs_rofs_super_block { unsigned int magic1; // 0x81006e6a unsigned int future; char igs_info[64]; unsigned int size; unsigned int magic2; // 0xD4AA2682 unsigned int flags; unsigned int padding; cramfs_info fsid; char name[64]; cramfs_inode root[2]; }; igs_rofs_super_block File;将上述文件header,移植到cramfs-tools和squashfs-tools,就可以完美提取文件系统,并且还可以发现文件是否损坏。我已经将代码上传至github。https://github.com/gorgiaxx/igs-toolkits
这个版本提取难度不高,但下个版本E3000就用上加密了。
玩家 @novacosmic00 之前请我帮忙提取 GoGoBall 的文件,但他说他已经解密了其他游戏的文件。唯独 GoGoBall 解不了。用我修改的工具尝试提取,发现文件CRC不对,但还是能提取出大部分。
在我提取完固件之后,和玩家 @novacosmic00 沟通,才知道两年前有人研究过这个,并写了提取脚本。
https://github.com/batteryshark/igstools/tree/main/scripts
作者可能是根据文件系统内容,找出地址规律反推结构体的,这也是一种办法,但缺点是提取出来的文件可能比较混乱,而且无法检测CRC错误。
shadow-shadow文件,默认都清空了root密码
root:x:13130:0:99999:7::: test::13074:0:99999:7:::这是原始的文件
root:$1$qAw/5vcb$x9rPCAwLMdRBQXwlq1zG70:13130:0:99999:7::: test:$1$JjP1oLAJ$xilZIedv3S3jbs8oTZAad1:13074:0:99999:7::: 反破解/PM2008v2/PM2008v2,符号去除了,并且根据字符串特征,也像是正常的loader
但实际上,如果在自己电脑运行,他会把你分区前512k写0。我当时居然运行了,还好我没给root,而且硬盘是NVME的。谁能想到这个18年前的游戏,还有这一手🙈。以前从来没接触过这个领域,据说街机的反破解有很多自杀式逻辑。
下一篇文章将分析游戏内部的保护机制
参考