吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 495|回复: 9
收起左侧

[C&C++ 原创] xpack压缩壳源码,学习纯汇编写代码

  [复制链接]
西风 发表于 2026-7-6 12:36
本帖最后由 苏紫方璇 于 2026-7-7 01:21 编辑

20年前用纯汇编写的压缩壳,以压缩为主,有点小花、小反调试都很简单,
主要功能就是加密原EXE,压缩体积,模拟windows填充api,
我不确定在现在的64位系统能工作正常哈,我提供的是源代码,可以自己修改一下。
界面很简陋,如图,在20年前能用纯汇编写还是比较牛逼的了。
ScreenShot_2026-07-06_122540_508.png
当然好汉不提当年勇,这款壳原来在《黑客防线》中有刊登。
在发表模块中没有找到汇编模块,就放C模块发表了,请版主见谅。
关键代码如下,shellcode的部分,这款壳没有linkcode。
[Asm] 纯文本查看 复制代码
BigFlower
        call myjmp
        add        esi,dword ptr[ebx+(filehandle-shellcode1)] ;;;;导入表真实地址
        BigFlower
        mov dword ptr[ebx+(ApiCount-shellcode1)],0
       
        call CheckGetProc
        BigFlower
        call myjmp
        ;单独加密一个DLL

                push        ebx
                push        esi
                mov        eax,dword ptr[esi+12]
                add        eax,dword ptr[ebx+(filehandle-shellcode1)] ;;;dll名字
                push        eax
                BigFlower
                call        dword ptr[ebx+(secondfun-shellcode1)]  ;;;getmodulehandle
                .if        eax==0
                        pop        esi
                        pop        ebx
                        push        ebx
                        push        esi
                        mov        eax,dword ptr[esi+12]
                        add        eax,dword ptr[ebx+(filehandle-shellcode1)]
                        push        eax
                        BigFlower
                        call         dword ptr[ebx+(thirdfun-shellcode1)]  ;;;loadlibrary
                       
                       
                .endif
                pop        esi
                pop        ebx
                push        esi
               
               
                mov        edx,eax  ;;;;;edx存放dll模块句柄
                mov    [ebx+(DllName-shellcode1)],eax
                mov        eax,dword ptr[esi]  
                mov        edi,dword ptr[esi+16]
                add        edi,dword ptr[ebx+(filehandle-shellcode1)] ;;;;IAMGE_THUNK_DATA真实地址
                .if        eax==0
                        mov        eax,dword ptr[esi+16]
                       
                .endif
                mov        esi,eax
                BigFlower
                add        esi,dword ptr[ebx+(filehandle-shellcode1)] ;;;IMAGE_THUNK_DATA真实地址
                .while        dword ptr[esi]!=0
                        .if        (dword ptr[esi])&80000000h ;;;不为零则以序号输入
                       
                                mov        eax,dword ptr[esi]
                                and        eax,7fffffffh
                                push        ebx
                                push        edx
                                push         esi
                                push        edi
                               
                                push        eax
                                push        edx
                                call        GetApiAddress2
                                pop        edi
                                mov        dword ptr[edi],eax
                                pop        esi
                                pop        edx
                                pop        ebx
                        .else       
                                mov        eax,dword ptr[esi]
                                add        eax,2
                                push        ebx
                                push        edx
                                push         esi
                                push        edi
                                add        eax,[ebx+(filehandle-shellcode1)]
                               
               

                                push   eax
                                push   edx
                                ;call  GetApiAddress2
                  
                                call dword ptr [ebx+(aGetProcAddress-shellcode1)]

                                pop        edi
                              
                               
                           ;检测是否有硬件断点
                              
                                               
                                ;加密IAT
       
                  
                                BigFlower
                                       
                              
                     
                                     
                                mov ecx,[ebx+(ApiCount-shellcode1)]
                                mov    [ebx+(IATAddr-shellcode1)+ecx],eax
                                lea edx,[ebx+(AddrDeCryIAT-shellcode1)+ecx]
                                mov word ptr [edx],158bh
                                
                                mov eax, (IATAddr-shellcode1)
                                add eax,ecx
                                add eax,ebx
                                mov [edx+2], eax
                                mov word ptr [edx+6],0e2ffh
                                mov  eax,edx
                                add ecx,08h       
                                mov [ebx+(ApiCount-shellcode1)],ecx
                                 
                                         
                                 
                                
                                  BigFlower
                                
                                mov        dword ptr[edi],eax
      
                                pop        esi
                                pop        edx
                                pop        ebx
               
                        BigFlower
                        .endif
                        add        esi,4
                        add        edi,4
                       
                BigFlower       
                .endw
                pop        esi
                add        esi,20

下载地址:通过网盘分享的文件:xfpack3.rar
链接: https://pan.baidu.com/s/1RR0SX28ChRSVDUoy5G3GcA 提取码: 52pj
--来自百度网盘超级会员v8的分享

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
zjdals + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

bF6eUh3 发表于 2026-7-6 19:44
Snipaste_2026-07-06_19-43-56.png
贴代码段可以用这个

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
苏紫方璇 + 1 + 1 热心回复!

查看全部评分

renpeng009 发表于 2026-7-6 21:51
YYSUNFENG 发表于 2026-7-6 23:39
小二神 发表于 2026-7-7 00:44
压缩后打不开啊
redapple2015 发表于 2026-7-7 08:30
大神能出个成品可以用的
 楼主| 西风 发表于 2026-7-7 08:33
bF6eUh3 发表于 2026-7-6 19:44
贴代码段可以用这个

谢谢指导
kulouxiaohai 发表于 2026-7-7 11:27
能讲一下原理吗?
 楼主| 西风 发表于 2026-7-8 09:07
kulouxiaohai 发表于 2026-7-7 11:27
能讲一下原理吗?

加壳阶段
1. PE校验(IsPe)
首先检查目标文件是否为合法的PE可执行文件:
检查DOS头的MZ签名(0x5A4D)
通过e_lfanew字段找到PE签名位置,检查PE签名(0x4550)
确认文件不是DLL(检查IMAGE_FILE_DLL标志位)
2. 加载PE文件(LoadPe)
将磁盘上的PE文件按照"PE加载器"的方式展开到内存中:
(a)创建文件映射
使用CreateFileMapping + MapViewOfFile将文件映射到内存,得到FileBuffer(磁盘文件的内存映射)。
(b)检测是否已加壳
在PE头前5字节检查加壳标志46586c6ch + 64h(即字符串"llXFd"),如果已加壳则拒绝重复加壳。
(c)按PE加载器方式展开区块
将每个区块的SizeOfRawData字节从FileBuffer(磁盘布局)复制到MapOfFile(内存布局)中对应VirtualAddress的位置,使MapOfFile中的数据与程序运行时的内存映像一致。
(d)VirtualSize对齐
将每个区块的VirtualSize按SectionAlignment向上对齐,为后续区块合并做准备。
(e)区块合并
将所有非资源区块合并为一个大区块。将后续区块的VirtualSize累加到第一个区块的VirtualSize上,然后删除被合并的区块表项,修改PE头的NumberOfSections。资源区块通过数据目录项中资源目录的RVA来识别(而非区块名),避免因区块名被修改而误判。
(f)清除重定位数据(EraseReloc)
读取数据目录项IMAGE_DIRECTORY_ENTRY_BASERELOC获取重定位表的RVA和大小,将MapOfFile中对应区域全部清零,并清除数据目录项。原因是加壳后OEP改变,原重定位数据已无效,壳代码会在运行时自行处理重定位。
(g)保存附加数据(MoveExData)
计算最后一个区块末尾到文件末尾的差值,如果存在附加数据(Overlay),将其保存到lpExData缓冲区,最后追加写回加壳后的文件。
3. 压缩区块(PackFile)
逐个处理每个区块,根据区块类型采取不同策略:
使用aPlib压缩算法(aP_pack函数)压缩区块数据
压缩后按FileAlignment对齐
修正区块表中的PointerToRawData和SizeOfRawData
保存压缩信息PackSecInfo:每项12字节,包含[原始大小, VirtualAddress, 压缩后大小]
将压缩后的数据写入FileBuffer
4. 修复与写入(Repair)
组装壳代码并修改PE头:
(a)修复shellcode0
将shellcode0复制到临时缓冲区shell0buf
修复shellcode0中自建导入表的偏移:所有RVA加上SizeOfImage + NotPackResDataSize基址,使其指向新区块中的正确位置
调用MoveImport将原程序的导入表复制到shellcode0的NewImport区域
(b)MoveImport——导入表移动
原程序的导入表移动到壳代码中,否则压缩后原导入表位置的数据被破坏,程序无法正常加载DLL。
计算原导入表中DLL的数量
逐个复制IMAGE_IMPORT_DESCRIPTOR结构,清零OriginalFirstThunk、TimeDateStamp、ForwarderChain
移动API函数名字符串和DLL名字符串到新位置
处理序号导入(最高位为1)和名称导入两种情况
所有RVA加上SizeOfImage + NotPackResDataSize基址
压缩shellcode1
使用aPlib压缩shellcode1,压缩后的数据将放入新区块。
修改PE头
新增名为.llXFd的区块,设置属性为0xE0000020(可读可写可执行含代码)
修改OEP指向新区块的VirtualAddress(即SizeOfImage + NotPackResDataSize),程序启动时先执行shellcode0
修改导入表RVA指向shellcode0中的自建导入表
增加NumberOfSections
增加SizeOfImage
清除重定位、LOAD_CONFIG、绑定导入、IAT数据目录项
写入加壳标志
在PE头前5字节写入llXFd,用于检测是否已加壳。
写入文件


运行时壳代码
壳代码分为两阶段:shellcode0(第一阶段,自举解压)和shellcode1(第二阶段,完整还原)。

第一阶段:shellcode0
shellcode0是加壳后程序的入口点,它的任务很简单——自举并解压shellcode1。

(a)地址重定位

asm
call mystart0        ; 将下一条指令地址压栈
mystart0:
pop  ebx             ; ebx = mystart0的运行时地址
sub  ebx, mystart0-shellcode0  ; ebx = shellcode0的真实基地址
这是经典的call/pop重定位技术,用于获取shellcode0在内存中的实际加载地址,后续所有地址引用都通过ebx + 偏移计算。

(b)自建导入表
shellcode0内嵌了一个精简的导入表,仅包含5个API:

GetProcAddress——获取API地址
LoadLibraryA——加载DLL
GetModuleHandleA——获取已加载模块句柄
VirtualAlloc——分配内存
VirtualFree——释放内存
这个自建导入表的作用是:壳代码运行时需要调用这些API来完成解压和还原工作,但此时原程序的导入表尚未初始化,所以壳代码必须自带导入表。Windows PE加载器在加载程序时会处理这个自建导入表,使壳代码能够调用这5个API。

(c)分配内存并解压shellcode1

asm
push  PAGE_EXECUTE_READWRITE
push  MEM_COMMIT
push  edx                    ; shellcode1的原始大小
push  NULL
call  VirtualAlloc           ; 分配可执行内存
mov   esi, eax               ; esi = 解压目标地址
mov   edi, [ebx+shellbase]   ; 压缩数据源地址
add   edi, ebx
push  esi
push  edi
call  _aP_depack_asm         ; 解压shellcode1
ret                          ; 跳转到解压后的shellcode1
(d)内嵌aPlib解压算法
_aP_depack_asm是aPlib解压算法的纯汇编实现,直接嵌入在shellcode0中。它使用基于位流的LZ77变体压缩算法,通过getbit/getgamma子程序逐位解码,支持字面量复制、短匹配、长匹配等模式。

第二阶段:shellcode1
shellcode1是壳代码的核心,负责还原原始程序的所有数据并跳转到OEP。

(a)获取kernel32基地址
shellcode1首先需要获取kernel32.dll的基地址,有两种方式:

从栈上获取(shellcode0传入的参数)
调用GetKernelBase函数:从栈顶地址向下搜索MZ/PE签名
(b)解析导出表获取API
通过GetApiAddress2函数解析kernel32的导出表,获取以下API:

GetModuleHandleA、LoadLibraryA、GetProcAddress、VirtualProtect
加载user32.dll,获取MessageBoxA、IsIconic、DestroyWindow、MessageBeep、LoadImageW
(c)将EBX写入kernel32的DOS头保留域
shellcode1通过VirtualProtect修改kernel32的DOS头0x1C处(保留域)的内存保护属性,将EBX(shellcode1的基地址)写入该位置。这样后续的API重定向跳板代码可以通过PEB遍历获取kernel32基地址后,从0x1C处读回EBX,实现间接寻址。

(d)用户选项处理
检查dFlag标志:

如果设置了MsgBox_FLAG(值为2),先弹出一个MessageBox提示框
否则直接进入还原流程
(e)从shellcode0获取API地址
shellcode0的5个thunk已经被PE加载器填充了真实API地址,shellcode1将这5个地址复制到自己的firstfun~fifthfun变量中,供后续使用。

(f)获取模块基址
调用GetModuleHandleA(NULL)获取程序自身的基地址(filehandle),后续所有RVA都需要加上这个基址才能得到真实地址。

(g)解压区块数据
这是还原的核心循环:

对每个需要解压的区块:
  1. VirtualAlloc分配内存(大小=原始大小)
  2. _aP_depack_asm解压数据到分配的内存
  3. 将解压后的数据复制回原位置(filehandle + VirtualAddress)
  4. VirtualFree释放临时内存
(h)初始化导入表(IAT填充)
遍历原程序的导入表,对每个DLL:

先尝试GetModuleHandleA获取DLL句柄(DLL可能已加载)
如果失败,调用LoadLibraryA加载DLL
遍历该DLL的所有导入项:
序号导入(最高位为1):通过GetApiAddress2按序号获取API地址获取
名称导入:通过GetProcAddress按名称获取
将获取的API地址写入IAT(FirstThunk指向的位置)
(i)IAT加密(API重定向)
这是壳的重要保护机制。对于kernel32.dll的API,shellcode1不直接将API地址写入IAT,而是:

将真实API地址保存到IATAddr数组
在AddrDeCryIAT区域动态生成一段跳板代码:
asm
mov eax, [IATAddr+offset]  ; 机器码: 8B 15 [addr]
jmp eax                     ; 机器码: FF E0
将IAT中的地址替换为跳板代码的地址
这样,程序调用API时实际上是先执行跳板代码,再间接跳转到真实API地址。静态分析工具无法直接从IAT中读取API地址。

此外,对特定API(IsIconic、DestroyWindow、MessageBeep、LoadImageW)有专门的跳板代码(AddrDeCryIsIconic等),这些跳板通过PEB遍历获取kernel32基地址,再从0x1C处读取EBX,然后间接跳转。

(j)抽代码入壳
扫描OEP附近代码中mov eax, imm32(机器码0xB8后跟4字节立即数,模式000001B8h)的指令,将其替换为CALL ShellCallAddress(机器码0xE8后跟4字节相对偏移)。ShellCallAddress处的代码只是简单地push 1; pop eax; ret,模拟mov eax, 1的效果(这是我当初学习VM的基础时的想法)。这破坏了静态分析中的代码模式。

(k)变形解码
对OEP附近0x100h偏移处的代码进行XOR解密(密钥为key变量的值),还原被加密的原始代码。

(l)跳转到OEP

asm
mov  eax, [ebx+entrypoint]
add  eax, [ebx+filehandle]
push eax
ret   ; 跳转到原始入口点
至此,原始程序完全还原,开始正常执行。

反调试保护机制
xfpack3的小花小调试保护:

1. 花指令(Flower / BigFlower)
Flower宏:插入jmp $+3; db 0E8h的垃圾指令序列。jmp $+3跳过下一个字节,而0E8h是CALL指令的操作码,反汇编器会将其误认为CALL指令,导致反汇编结果错误。

BigFlower宏:更复杂的花指令,包含:

pushad/pushfd保存所有寄存器
乱序字节(看似CALL实则被跳过)
调用BigFlowerP函数处理跳转
调用FindHardW检测硬件断点
调用myjmp检测INT3断点
popfd/popad恢复寄存器
2. 硬件断点检测(FindHardW)
通过INT 1指令触发单步异常,在SEH异常处理函数中检查并清零调试寄存器DR0-DR3:

asm
int  1h              ; 触发单步异常
; SEH handler:
mov  [eax+04h], 0    ; 清零DR0
mov  [eax+08h], 0    ; 清零DR1
mov  [eax+0Ch], 0    ; 清零DR2
mov  [eax+10h], 0    ; 清零DR3
如果调试器设置了硬件断点,DR0-DR3不为零,清零后断点失效。

3. INT3断点检测(myjmp)
扫描后续0x60h字节,检测是否存在0xCC(INT3)或0x90(NOP)断点:

asm
mov  ecx, 60h
.while ecx!=0
  mov  al, [edx+ecx]
  cmp  al, 0CCh      ; INT3断点
  .if Zero?
    push [edx+77h]   ; 跳到异常处理地址
    ret
  .endif
  cmp  al, 90h       ; NOP断点
  .if Zero?
    push [edx+77h]
    ret
  .endif
  dec  ecx
.endw
如果检测到断点,将密钥key设为0x99,导致后续XOR解密使用错误密钥,程序崩溃。

4. GetProcAddress下断检测(CheckGetProc)
检测GetProcAddress函数前0x10h字节是否有0xCC断点,防止调试者通过断点GetProcAddress来监控API调用。

5. IAT加密(API重定向)
如前所述,将kernel32的API地址替换为动态生成的跳板代码,使静态分析无法直接获取API地址。

6. 抽代码入壳
将原代码中的mov eax, imm32指令替换为CALL跳板,破坏静态分析中的代码模式识别。


项目中还包含几个独立的加密/变形引擎:

1. BPE32多态变形引擎(BPE.ASM)
生成随机化的解密器代码,包含:

随机寄存器选择
垃圾指令插入(1/2/3/5字节的无效指令+dummy jump/call)
多种等价指令替换
XOR加密+递增密钥
2. PER逐字节加密引擎(PER.ASM)
by yoda,14种可逆操作对(NOP、STC/CLC、INC/DEC AL、ADD/SUB AL、XOR AL、ROL/ROR AL、自修改代码等),生成逐字节加密/解密指令对。

3. CryptStuff(CryptStuff.ASM)
yoda Protector的完整PE加密壳实现,包含PER加密、CRC校验、SI检测、反dump、API重定向、PE头擦除、OEP跳转SEH等完整功能。

大概就这样,都是很基础的实现。
kulouxiaohai 发表于 2026-7-8 11:31
西风 发表于 2026-7-8 09:07
加壳阶段
1. PE校验(IsPe)
首先检查目标文件是否为合法的PE可执行文件:

谢谢,按照这个讲解学习一下。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-19 07:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表