Skip to content

Instantly share code, notes, and snippets.

@hyrious
Created July 8, 2019 03:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyrious/94b0126cdf12239af73d9afabb29a080 to your computer and use it in GitHub Desktop.
Save hyrious/94b0126cdf12239af73d9afabb29a080 to your computer and use it in GitHub Desktop.
Sample RGSS Console Using MailSlot
# coding: utf-8
Font.default_name = ['等距更纱黑体 SC', '等线', 'SimHei']
class Module
public :include
end
if defined? Graphics.background_exec
Graphics.background_exec = true
else
# RGSS103J 0x2E48 \x46
# RGSS102J 100J 0x2E09 \x46
# RGSS104E 0x4FA4 \x5F
# RGSS200J 200E 202E 0x19AA \x35
# RGSS300 301 0x2712 \x5A
WriteProcessMemory = Win32API.new('kernel32','WriteProcessMemory','llpll','l')
DLL=Win32API.new('kernel32','LoadLibrary','p','l').call('RGSS301')
WriteProcessMemory.call(-1,DLL+0x2712,"\0",1,0)
end
class Api
def initialize dll, func
@dll, @func = dll.to_s, func.to_s
@instance = nil
end
def call *args
@comment = "#{@func}.call(#{args.map(&:inspect).join(', ')})"
# puts @comment
(@instance ||= create_instance(*args)).call(*args)
end
def create_instance *args
@imports = args.map { |e| Integer === e ? 'L' : 'p' }
Win32API.new(@dll, @func, @imports, 'i')
end
def declaration
return '' unless @instance and @comment
imports = @imports.empty? ? 'v' : @imports.join
[ ["# ", @comment].join,
["#{@func} = ",
"Win32API.new(#{@dll.inspect}, ",
"#{@func.inspect}, ",
"#{imports.inspect}, 'i')"].join
].join("\n")
end
class Dll
def initialize dll
@dll = dll
end
def method_missing func, *args
Api.callapi @dll, func, *args
end
end
def self.callapi dll, name, *args
((@_win32api ||= {})[[dll, name]] ||= Api.new(dll, name)).call(*args)
end
def self.export_apis
return unless @_win32api
puts
@_win32api.each do |(dll, name), api|
puts api.declaration
end
puts
end
def self.method_missing dll
Dll.new dll
end
end
class MailSlot
def self.create
new "\\\\.\\mailslot\\rgss_#{$$}_#{rand(0x100000000).to_s(36)}"
end
MAILSLOT_NO_MESSAGE = MAILSLOT_WAIT_FOREVER = INVALID_HANDLE_VALUE = -1
def initialize name
@name = name
@handle = Api.kernel32.CreateMailslot name, 0, MAILSLOT_WAIT_FOREVER, 0
@disposed = false
if @handle == INVALID_HANDLE_VALUE
puts "CreateMailslot failed with #{Api.kernel32.GetLastError()}."
@disposed = true
end
end
attr_reader :name, :handle
def disposed?
@disposed
end
FALSE = 0
def dispose
@disposed = true
if Api.kernel32.CloseHandle(@handle) == FALSE
puts "CloseHandle failed with #{Api.kernel32.GetLastError()}."
end
end
def read
result = []
return result if disposed?
event = Api.kernel32.CreateEvent(0, 0, 0, 0)
if event == 0
puts "CreateEvent failed with #{Api.kernel32.GetLastError()}."
return result
end
ov = [0, 0, 0, 0, event].pack('L5')
next_len = [0].pack('L')
count = [0].pack('L')
ret = Api.kernel32.GetMailslotInfo(@handle, 0, next_len, count, 0)
if ret == FALSE
puts "GetMailslotInfo failed with #{Api.kernel32.GetLastError()}."
return result
end
if next_len == MAILSLOT_NO_MESSAGE
return result
end
while count.unpack('L')[0] > 0
size = next_len.unpack('L')[0]
read = [0].pack('L')
# should it cause error, use GlobalAlloc(0x40, next_len.unpack('L')[0])
buffer = [].pack("x#{size}")
ret = Api.kernel32.ReadFile(@handle, buffer, size, read, ov)
if ret == FALSE
puts "ReadFile failed with #{Api.kernel32.GetLastError()}."
return result
end
result << buffer.unpack('Z*')[0]
ret = Api.kernel32.GetMailslotInfo(@handle, 0, next_len, count, 0)
if ret == FALSE
puts "GetMailslotInfo failed with #{Api.kernel32.GetLastError()}."
return result
end
end
if Api.kernel32.CloseHandle(event) == FALSE
puts "CloseHandle failed with #{Api.kernel32.GetLastError()}."
end
result
end
def pop
(@queue ||= []).push(*read).shift
end
def self.write name, message
handle = (@_clients ||= {})[name] ||= open(name)
written = [0].pack('L')
msg = [message].pack('Z*')
ret = Api.kernel32.WriteFile(handle, msg, msg.size, written, 0)
if ret == FALSE
puts "WriteFile failed with #{Api.kernel32.GetLastError()}."
end
end
def self.open name
handle = Api.kernel32.CreateFile(name, 0x40000000, 1, 0, 3, 0x80, 0)
if handle == INVALID_HANDLE_VALUE
puts "CreateFile failed with #{Api.kernel32.GetLastError()}."
end
handle
end
def self.dispose
@_clients.each_value { |handle| Api.kernel32.CloseHandle handle }
@_clients.clear
end
end
SlotServer = MailSlot.create
Api.shell32.ShellExecute 0, 0, 'Scripts\sample_console.exe', SlotServer.name, 0, 1
Graphics.update until client = SlotServer.pop
MailSlot.write client, "OK"
loop do
if msg = SlotServer.pop
puts "=> #{msg}".chomp
ret = 'nil'
begin
ret = eval(msg, TOPLEVEL_BINDING).inspect
rescue SystemExit
break
rescue Exception => e
ret = "#{e.class}: #{e}"
end
puts ">> #{ret}".chomp
MailSlot.write client, ret
end
Graphics.update
end
SlotServer.dispose
MailSlot.dispose
Api.export_apis
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
/* -fgnu89-inline */
#include <strsafe.h>
HANDLE hSlotServer, hSlotClient;
TCHAR SlotServer[80], SlotClient[80];
char tmp[10], template[] = "0123456789abcdefghijklmnopqrstuvwxyz";
BOOL RandSlotServerName(void) {
srand(time(0));
UINT i = (UINT) (((double) rand() / RAND_MAX) * INT_MAX);
int j = 0;
do {
tmp[j++] = template[i % 36];
i /= 36;
} while (i);
HRESULT fResult = StringCbPrintf(SlotServer, sizeof(SlotServer), TEXT("\\\\.\\mailslot\\rgss_%d_%s"), GetCurrentProcessId(), tmp);
if (SUCCEEDED(fResult)) {
return TRUE;
} else {
printf("StringCbPrintf failed with (HRESULT) %d.\n", fResult);
printf("See https://docs.microsoft.com/en-us/windows/win32/api/strsafe/nf-strsafe-stringcbprintfa for more details.\n");
return FALSE;
}
}
BOOL GetSlotClientName(char *cmd) {
HRESULT fResult = StringCbCopy(SlotClient, sizeof(SlotClient), cmd);
if (SUCCEEDED(fResult)) {
return TRUE;
} else {
printf("StringCbCopy failed with (HRESULT) %d.\n", fResult);
printf("See https://docs.microsoft.com/en-us/windows/win32/api/strsafe/nf-strsafe-stringcbcopya for more details.\n");
return FALSE;
}
}
BOOL MakeSlotServer(LPTSTR lpszSlotName) {
hSlotServer = CreateMailslot(lpszSlotName, 0, MAILSLOT_WAIT_FOREVER, 0);
if (hSlotServer == INVALID_HANDLE_VALUE) {
printf("CreateMailslot failed with %d.\n", GetLastError());
return FALSE;
}
return TRUE;
}
BOOL GetSlotClient(LPTSTR lpszSlotName) {
hSlotClient = CreateFile(lpszSlotName, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hSlotClient == INVALID_HANDLE_VALUE) {
printf("CreateFile failed with %d.\n", GetLastError());
return FALSE;
}
return TRUE;
}
BOOL ReadSlotServer(void) {
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
LPTSTR lpszBuffer;
TCHAR achID[80];
DWORD cAllMessages;
HANDLE hEvent;
OVERLAPPED ov;
cbMessage = cMessage = cbRead = 0;
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
ov.Offset = ov.OffsetHigh = 0;
ov.hEvent = hEvent;
fResult = GetMailslotInfo(hSlotServer, 0, &cbMessage, &cMessage, 0);
if (!fResult) {
printf("GetMailslotInfo failed with %d.\n", GetLastError());
return FALSE;
}
if (cbMessage == MAILSLOT_NO_MESSAGE) {
// printf("waiting...\n");
return TRUE;
}
cAllMessages = cMessage;
while (cMessage) {
lpszBuffer = (LPTSTR) GlobalAlloc(GPTR, cbMessage);
if (lpszBuffer == NULL)
return FALSE;
lpszBuffer[0] = '\0';
fResult = ReadFile(hSlotServer, lpszBuffer, cbMessage, &cbRead, &ov);
if (!fResult) {
printf("ReadFile failed with %d.\n", GetLastError());
GlobalFree((HGLOBAL) lpszBuffer);
return FALSE;
}
printf("=> %s\n>> ", lpszBuffer);
GlobalFree((HGLOBAL) lpszBuffer);
fResult = GetMailslotInfo(hSlotServer, 0, &cbMessage, &cMessage, 0);
if (!fResult) {
printf("GetMailslotInfo failed with %d.\n", GetLastError());
return FALSE;
}
}
CloseHandle(hEvent);
return TRUE;
}
BOOL WriteSlotClient(LPTSTR lpszMessage) {
BOOL fResult;
DWORD cbWritten;
fResult = WriteFile(hSlotClient, lpszMessage, (lstrlen(lpszMessage) + 1) * sizeof(TCHAR), &cbWritten, 0);
if (!fResult) {
printf("WriteFile failed with %d.\n", GetLastError());
return FALSE;
}
return TRUE;
}
DWORD WINAPI ServerThread(LPVOID lpParam) {
while (ReadSlotServer()) {
Sleep(100);
}
return 0;
}
#define CONSOLE_BUFFER_SIZE 1024
TCHAR szBuffer[CONSOLE_BUFFER_SIZE];
int main(int argc, char **argv) {
if (argc == 1) {
printf("usage: %s \\\\.\\mailslot\\<rgss-slot-server-name>\n", argv[0]);
return 0;
}
SetConsoleTitle(TEXT("Sample Console"));
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode;
if (GetConsoleMode(hOutput, &dwMode)) {
SetConsoleMode(hOutput, dwMode | ENABLE_ECHO_INPUT);
}
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwRead;
if (GetSlotClientName(argv[1]) && RandSlotServerName()) {
printf("\n> client: %s\n> server: %s\n\n", SlotClient, SlotServer);
} else {
return EXIT_FAILURE;
}
if (!MakeSlotServer(SlotServer)) {
return EXIT_FAILURE;
}
if (!GetSlotClient(SlotClient)) {
return EXIT_FAILURE;
}
WriteSlotClient(SlotServer);
DWORD dwThreadId;
HANDLE hThread = CreateThread(NULL, 0, ServerThread, NULL, 0, &dwThreadId);
while (TRUE) {
ZeroMemory(szBuffer, CONSOLE_BUFFER_SIZE);
if (ReadConsole(hInput, szBuffer, CONSOLE_BUFFER_SIZE, &dwRead, NULL)) {
WriteSlotClient(szBuffer);
if (strncmp(szBuffer, "exit", 4) == 0) {
break;
}
} else {
printf("ReadConsole failed with %d.\n", GetLastError());
}
}
TerminateThread(hThread, 0);
CloseHandle(hSlotClient);
CloseHandle(hSlotServer);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment