美文网首页
RGSS 中执行机器码

RGSS 中执行机器码

作者: hyrious | 来源:发表于2017-10-14 10:50 被阅读14次

    给大家认识一下,这是我们今天的主角↓

    CallWindowProc

    CallWindowProc = Win32API.new 'user32', 'CallWindowProc', 'pLLLL', 'i'
    

    这个 API 接受 5 个参数,p 是机器码(字符串)的地址,四个 L 是可选参数,返回值由 %eax 带出。

    符号 含义 说明
    p *pointer RGSS 为 32 位运行环境,在这里是 4 字节的
    L unsigned int pack 的时候正负数无关,例如 [-1].pack('i')[-1].pack('L') 得到的结果是一样的
    i signed int unpack 的时候正负数有关,[-1].pack('L').unpack('L') 会得到 2^32-1

    A+B

    考虑一个汇编子程,参数为两个 int,将求和后的结果保存到 %eax

    sum:
      push %ebp
      mov %esp,%ebp
      mov 8(%ebp),%eax  # 第一个参数
      add 12(%ebp),%eax # 第二个参数,直接加给 %eax
      leave
      ret $8 # 两个 int = 4 * 2
    

    实际上堆栈框架(push ebp ~ leave)是有点浪费字节的,我们把它去掉:

    sum:
      mov 4(%esp),%eax # 第一个参数
      add 8(%esp),%eax # 第二个参数,直接加给 %eax
      ret $8 # 两个 int = 4 * 2
    
    指令 机器码
    mov 源(右)是寄存器 0x8b ModR/M SIB Disp Imm
    add 源(右)是寄存器 0x03 ModR/M SIB Disp Imm
    ret imm16 0xc2 Imm16

    上面这段子程的机器码可以翻译为:

    [
      0x8b, 0104,0044, 4,  # [0] mov 4(%esp), %eax
      0x03, 0104,0044, 8,  # [1] add 8(%esp), %eax
      0xc2,          8,0,  #     ret $8
    ].pack('C*')
    

    更多机器码参考这里

    由于我们一开始写了四个 L 参数的声明,这里修正为 ret $16,然后我们试一下这个 API:

    p CallWindowProc.call [
      0x8b, 0104,0044, 4,  # [0] mov 4(%esp), %eax
      0x03, 0104,0044, 8,  # [1] add 8(%esp), %eax
      0xc2,         16,0,  #     ret $16
    ].pack('C*'), 3, 5, 0, 0
    

    (RGSS3 请使用 msgbox 或者打开控制台选项来看输出)

    Bitmap

    下面我们泄露一个 Bitmap 的内存结构:

    [0,0,0,0,[0,0,[0,0,0,0,pRData]]]
    

    RData 里存的是每个像素的 BGRA 信息。另外,RGSS 中 object_id * 2 是 Bitmap 对象的真实地址位置。

    0 代表四字节数据并且不关心,我们要得到这个 pRData

    class Bitmap
      GETADDR = [
        0x8b, 0104,0044, 4,  # mov  4(%esp), %eax
        0x8b, 0100,     16,  # mov 16(%eax), %eax
        0x8b, 0100,      8,  # mov  8(%eax), %eax
        0x8b, 0100,     16,  # mov 16(%eax), %eax
        0xc2,         16,0,  # ret $16
      ].pack('C*')
      def addr
        @_addr ||= CallWindowProc.call GETADDR, object_id * 2, 0, 0, 0
      end
    end
    

    有了位图数据的首地址,就可以开始搞事了:

    Pixel

    首先泄露一下位图数据的内存形式为:

    BGRABGRABGRABGRABGRABGRABGRABGRA...
    

    width * heightBGRA

    考虑一个简单的反色算法:把每个 pixel 的 BGR 数据都取反。

    C 语言形式如下:

    for (int i = 0; i < length; ++i)
      data[i * 4] ^= 0x00FFFFFF; // AARRGGBB
    

    不难写出这样的代码:

    def callproc code, a = 0, b = 0, c = 0, d = 0
      code = code.pack 'C*' if Array === code
      CallWindowProc.call code, a, b, c, d
    end
    class Bitmap
      INVERSE = [
        0x8b, 0104,0044, 4,  # mov  4(%esp), %eax   addr
        0x8b, 0114,0044, 8,  # mov  8(%esp), %ecx   length
        0x81, 0060,   0xff,0xff,0xff,0x00,
                             # xorl  (%eax), 0x00FFFFFF # 0+1|4&5-6^
        0x83, 0300,      4,  # add       $4, %eax
        0xe2,          -11,  # loop     -11
        0xc2,         16,0,  # ret      $16
      ].pack('C*')
      def inverse!
        callproc INVERSE, addr, width * height
        self
      end
    end
    
    反色

    下面我们再写一个,伪色差效果:把整个图的某个通道整体左/右移 offset 个像素。

    C 代码类似下面这样:

    // phase  通道,假设一定是 0,1,2,3 中的一个
    // offset 偏移像素距离,可能为负数
    for (int i = 0; i < length; ++i)
      data[i + phase] = data[i + offset * 4 + phase];
    // 上面一定会产生越界错误,我们修正一下
    if (offset == 0) return;
    length -= abs(offset);
    if (offset > 0)
      for (int i = 0; i < length; ++i)
        data[i + phase] = data[i + offset * 4 + phase];
    else // offset < 0
      for (int i = length; i > 0; --i)
        data[i - offset * 4 + phase] = data[i + phase];
    

    出来的机器码大概是这个样子:

    class Bitmap
      SIMPLEABERRATION = [
        0x8b, 0104,0044, 4,  # [0] mov  4(%esp), %eax   addr
        0x8b, 0114,0044, 8,  # [1] mov  8(%esp), %ecx   length
        0x8b, 0164,0044,12,  # [2] mov 12(%esp), %esi   offset (can be neg)
        0x03, 0104,0044,16,  # [3] add 16(%esp), %eax   phase (% 4)
                             # ------------------------------- #
        0xbb,      4,0,0,0,  #     mov       $4, %ebx          #
        0x83, 0376,      0,  #     cmp       $0, %esi          #
        0x74,           23,  # .-- je        23                #
        0x7f,           10,  # |.- jg        10                #
        0x8d, 0104,0210,-4,  # ||  lea -4(%eax,%ecx,4),%eax    #
        0xf7, 0333,          # ||  neg    %ebx                 #
        0x01, 0361,          # ||  add    %esi , %ecx          #
        0xeb,            2,  # ||  jmp        2             -. #
        0x29, 0361,          # |'- sub    %esi , %ecx        | #
        0x8a, 0024,0260,     # |.- movb  (%eax,%esi,4),%dl  -' #
        0x88, 0020,          # ||  movb    %dl ,(%eax)         #
        0x01, 0330,          # ||  add    %ebx , %eax          #
        0xe2,           -9,  # |'- loop      -9                #
        0xc2,         16,0,  # '-- ret      $16                #
      ].pack('C*')
      def simple_aberration! offset = 5, phase = 2 # Red [B,G,R,A][phase]
        callproc SIMPLEABERRATION, addr, width * height, offset, phase % 4
        self
      end
    end
    
    伪色差

    小朋友们学会了吗 ;)

    相关文章

      网友评论

          本文标题:RGSS 中执行机器码

          本文链接:https://www.haomeiwen.com/subject/cllxuxtx.html