Related
I'm making the pac-man with the winapi32, and using XAudio2 API for playing multiple sounds simultanueosly. I played my sound file pacman_beginning.wav, pacman_death.wav, pacman_chomp.wav, pacman_eatfruit.wav, pacman_eatghost.wav, pacman_intermission.wav, pacman_siren.wav, and all of them worked fine, but pacman_siren.wav (I download them from Pac-Man Sounds, and Pacman Sound Effects, and pacman_siren.wav is renamed from 8d82b5_Pacman_Siren_Sound_Effect.wav ).
the following is part of my code(for XAudio2, I referred to How to: Play a Sound with XAudio2
)
PacmanApp.hpp:
////////////////////
// PacmanApp //
////////////////////
# include<Windows.h>
# include<functional>
# include<xaudio2.h>
# include<vector>
# include"resource.h" // `IDB_BITMAP1` is defined.
class PacmanApp {
struct AudioData {
BYTE* pDataBuffer;
DWORD dwChunkSize;
IXAudio2SourceVoice* pSourceVoice;
WAVEFORMATEXTENSIBLE wfx;
};
using Delegate = std::function<void(HDC,HDC)>;
// the window procedure.
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// find chunk from `fFile`.
static HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD dwChunkDataPosition);
// read chunk data from `fFile`.
static HRESULT ReadChunkData(HANDLE hFile, void* buffer, DWORD bufferSize, DWORD bufferoffset);
private:
// for message loop.
HINSTANCE hInstance = NULL;
HWND hwnd = NULL;
WNDCLASS wc = { 0 };
MSG msg = { 0 };
// for rendering.
HBITMAP hBM = NULL; // for pac-man sprite.
HBITMAP hBM2 = NULL; // for back-buffer.
RECT rect;
BITMAP bm;
// for application
Delegate callbackFn = nullptr; // callback function.
bool keyState[255] = { 0 }; // for key-down events.
bool isTimeout[256] = { 0 }; // for timer events.
// for sound.
IXAudio2* pXAudio2 = nullptr;
IXAudioMasteringVoice* pMasterVoice = nullptr;
XAUDIO2_BUFFER buffer = { 0 };
std::vector<AudioData> audioList;
private:
PacmanApp() = default;
PacmanApp(const PacmanApp&) = delete;
PacmanApp(PacmanApp&&) = delete;
~PacmanApp() {
if(hBM) DeleteObject(hBM);
if(hBM2) DeleteObject(hBM2);
for(auto& i : audioList) {
delete[] i.pDataBuffer;
}
}
public:
static PacmanApp inst; // for single-ton.
// initialize the pacman application.
bool Init(HINSTANCE hInstance, int nWidth, int nHeight);
// load the desired sound file.
bool LoadSound(const wchar_t strFileName[]);
// start the sound identified by `sndID`.
bool StartSound(UINT sndID, float playLength);
// run the pacman application.
template<typename Functor>
void Run(Functor&& callbackFn, int nCmdShow, UINT fps){
if(this->callbackFn = std::forward<Functor>(callbackFn) ) {
ShowWindow(hwnd, nCmdShow);
SetTimer(hwnd, 256, (1000 / fps), NULL);
while(GetMessage(&msg, 0, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer(hwnd, 256);
}
}
/* omitted */
};
PacmanApp.cpp:
# include"PacmanApp.hpp"
#ifdef _XBOX //Big-Endian
# define fourccRIFF 'RIFF'
# define fourccDATA 'data'
# define fourccFMT 'fmt '
# define fourccWAVE 'WAVE'
# define fourccXWMA 'XWMA'
# define fourccDPDS 'dpds'
#endif
#ifndef _XBOX //Little-Endian
# define fourccRIFF 'FFIR'
# define fourccDATA 'atad'
# define fourccFMT ' tmf'
# define fourccWAVE 'EVAW'
# define fourccXWMA 'AMWX'
# define fourccDPDS 'sdpd'
#endif
// for single-ton.
PacmanApp PacmanApp::inst;
// initialize the pac-man application.
bool PacmanApp::Init(HINSTANCE hInstance, int nWidth, int nHeight) {
/* ommitted. RegisterClass, CreateWindowEx, etc is here. */
// init COM
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)) ) {
return false;
}
// create XAudio engine.
if (FAILED(XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR) ) ) {
return false;
}
// create mastering voice.
if (FAILED(pXAudio2->CreateMasteringVoice(&pMasterVoice) ) ) {
return false;
}
return true;
}
// find chunk from `fFile`.
HRESULT PacmanApp::FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition) {
HRESULT hr = S_OK;
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN)) {
return HRESULT_FROM_WIN32(GetLastError() );
}
DWORD dwChunkType;
DWORD dwChunkDataSize;
DWORD dwRIFFDataSize = 0;
DWORD dwFileType;
DWORD bytesRead = 0;
DWORD dwOffset = 0;
while (hr == S_OK) {
DWORD dwRead;
// read ChunkID.
if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL)) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
// read ChunkDataSize.
if(0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL) ) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
if (dwChunkType == fourccRIFF) {
dwRIFFDataSize = dwChunkDataSize;
dwChunkDataSize = 4;
// read RIFFType.
if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL) ) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
}
else {
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT) ) {
return HRESULT_FROM_WIN32(GetLastError() );
}
}
dwOffset += sizeof(DWORD) * 2;
if (dwChunkType == fourcc) {
dwChunkSize = dwChunkDataSize;
dwChunkDataPosition = dwOffset;
return S_OK;
}
dwOffset += dwChunkDataSize;
if (bytesRead >= dwRIFFDataSize) {
return S_FALSE;
}
}
return S_OK;
}
// read chunk data from `hFile`.
HRESULT PacmanApp::ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset) {
HRESULT hr = S_OK;
DWORD dwRead;
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN) ) {
return HRESULT_FROM_WIN32(GetLastError() );
}
if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL) ) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
return hr;
}
// load the desired sound file.
bool PacmanApp::LoadSound(const wchar_t strFileName[]) {
HANDLE hFile = CreateFile(
strFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (INVALID_HANDLE_VALUE == hFile) {
return false;
}
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN) ) {
CloseHandle(hFile);
return false;
}
DWORD dwChunkSize;
DWORD dwChunkPosition;
DWORD filetype;
WAVEFORMATEXTENSIBLE wfx = { 0 };
// check the file type, should be fourccWAVE or 'XWMA'
FindChunk(hFile, fourccRIFF, dwChunkSize, dwChunkPosition);
ReadChunkData(hFile, &filetype, sizeof(DWORD), dwChunkPosition);
if (filetype != fourccWAVE) {
CloseHandle(hFile);
return false;
}
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition);
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition);
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
ReadChunkData(hFile, audioList.back().pDataBuffer, dwChunkSize, dwChunkPosition);
CloseHandle(hFile);
return true;
}
// start the sound identified by `sndID`.
bool PacmanApp::StartSound(UINT sndID, float playLength) { // playLength is in [0, 1.0f]
if (sndID < audioList.size() ) {
auto& curAudio = audioList[sndID];
buffer.AudioBytes = curAudio.dwChunkSize; // size of the audio buffer in bytes.
buffer.pAudioData = curAudio.pDataBuffer; // buffer containing audio data.
buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after the buffer.
buffer.PlayBegin = 0;
buffer.PlayLength = curAudio.dwChunkSize * playLength;
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&curAudio.pSourceVoice, (WAVEFORMATEX*)&curAudio.wfx)) ) {
return false;
}
if (FAILED(hr = curAudio.pSourceVoice->SubmitSourceBuffer(&buffer) ) ) {
return false;
}
if (FAILED(hr = curAudio.pSourceVoice->Start(0) ) ) {
return false;
}
}
return true;
}
main.cpp:
# include"PacmanApp.hpp"
# include"PacmanObject.hpp"
/////////////////////
// wWinMain //
/////////////////////
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR nCmdLine,
_In_ int nCmdShow)
{
if(!PacmanApp::inst.Init(hInstance, 1400, 650) {
return 0;
}
/* omitted */
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_beginning.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_death.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_chomp.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_eatfruit.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_eatghost.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_intermission.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_siren.wav");
enum struct PacmanSound : UINT {
INTRO, DEATH, CHOMP, EAT_FRUIT, EAT_GHOST, INTERMISSION, SIREN
};
// main loop.
PacmanApp::inst.Run([&](HDC srcDC, HDC destDC) {
PacmanApp::inst.StartSound((UINT)PacmanSound::INTRO, 1.0f); // OK. rest of them is same ,but..
// PacmanApp::inst.StartSound((UINT)PacmanSound::SIREN, 1.0f); // only this statement returns false.
}, nCmdShow, 60);
}
In the beginning, I thought that LoadSound have some problem. but,
pacman_beginning.wav:
hex dump:
52 49 46 46 EA B5 00 00 57 41 56 45 66 6D 74 20 RIFF....WAVEfmt
10 00 00 00 01 00 01 00 11 2B 00 00 11 2B 00 00 .........+...+..
01 00 08 00 64 61 74 61 9A B5 00 00 80 7F 80 7F ....data........
WAV format reader output:
C:\Users\user>"C:\Users\user\Desktop\WAV format reader\x64\Debug\WAV format reader.exe" "C:\Users\user\Desktop\pacman_beginning.wav"
WAVE file C:\Users\user\Desktop\pacman_beginning.wav
riff 'WAVE', chunk 'fmt ', 16 bytes
format tag 0001 (PCM)
number of channels 1
samples per second 11025
avg bytes per second 11025
sample block size 1 bytes
bits per sample 8
when PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_beginning.wav"); is excuting:
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x10, dwChunkPosition => 0x14
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x0x0000b59a, dwChunkPosition => 0x0000002c
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
the LoadSound result:
auto& curAudio = audioList.back();
curAudio.dwChunkSize = 46490
curAudio.wfx.Format = {
.wFormatTag = 1,
.nChannels = 1,
.nSamplesPerSec = 11025,
.nAvgBytesPerSec = 11025,
.nBlockAlign = 1,
.wBitsPerSample = 8,
.cbSize = 0
}
pacman_death.wav:
hex dump:
52 49 46 46 68 42 00 00 57 41 56 45 4C 49 53 54 RIFFhB..WAVELIST
28 00 00 00 49 4E 46 4F 49 53 46 54 1C 00 00 00 (...INFOISFT....
57 48 41 4D 20 31 2E 33 31 20 62 79 20 41 6E 64 WHAM 1.31 by And
72 65 77 20 42 75 6C 68 61 6B 00 00 66 6D 74 20 rew Bulhak..fmt
10 00 00 00 01 00 01 00 11 2B 00 00 11 2B 00 00 .........+...+..
01 00 08 00 64 61 74 61 14 42 00 00 80 80 82 7F ....data.B......
WAV format reader output:
C:\Users\user>"C:\Users\user\Desktop\WAV format reader\x64\Debug\WAV format reader.exe" "C:\Users\user\Desktop\pacman_death.wav"
WAVE file C:\Users\user\Desktop\pacman_death.wav
riff 'WAVE', chunk 'fmt ', 16 bytes
format tag 0001 (PCM)
number of channels 1
samples per second 11025
avg bytes per second 11025
sample block size 1 bytes
bits per sample 8
when PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_death.wav"); is excuting:
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x10, dwChunkPosition => 0x44
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x00004214, dwChunkPosition => 0x0000005c
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
the LoadSound result:
auto& curAudio = audioList.back();
curAudio.dwChunkSize = 16916
curAudio.wfx.Format = {
.wFormatTag = 1,
.nChannels = 1,
.nSamplesPerSec = 11025,
.nAvgBytesPerSec = 11025,
.nBlockAlign = 1,
.wBitsPerSample = 8,
.cbSize = 0
}
pacman_siren.wav:
hex dump:
52 49 46 46 54 3B 01 00 57 41 56 45 66 6D 74 20 RIFFT;..WAVEfmt
10 00 00 00 01 00 01 00 22 56 00 00 44 AC 00 00 ........"V..D...
02 00 10 00 4C 49 53 54 28 00 00 00 49 4E 46 4F ....LIST(...INFO
49 47 4E 52 06 00 00 00 4F 74 68 65 72 00 49 53 IGNR....Other.IS
46 54 0E 00 00 00 4C 61 76 66 35 39 2E 32 37 2E FT....Lavf59.27.
31 30 30 00 64 61 74 61 00 3B 01 00 00 00 00 00 100.data.;......
WAV format reader output:
C:\Users\user>"C:\Users\user\Desktop\WAV format reader\x64\Debug\WAV format reader.exe" "C:\Users\user\Desktop\pacman_siren.wav"
WAVE file C:\Users\user\Desktop\pacman_siren.wav
riff 'WAVE', chunk 'fmt ', 16 bytes
format tag 0001 (PCM)
number of channels 1
samples per second 22050
avg bytes per second 44100
sample block size 2 bytes
bits per sample 16
when PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_siren.wav"); is excuting:
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x10, dwChunkPosition => 0x14
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x00013b00, dwChunkPosition => 0x0000005c
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
the LoadSound result:
auto& curAudio = audioList.back();
curAudio.dwChunkSize = 80640
curAudio.wfx.Format = {
.wFormatTag = 1,
.nChannels = 1,
.nSamplesPerSec = 22050,
.nAvgBytesPerSec = 44100,
.nBlockAlign = 2,
.wBitsPerSample = 16,
.cbSize = 0
}
every calls to LoadSound work correctly. so, I think there is not any WAVEFORMATEX structure corruption, or incorrect uninitialized data.SubmitSourceBuffer is failed when onlysndID = PacmanSound::SIREN:
bool PacmanApp::StartSound(UINT sndID, float playLength) { // sndID = 6 (=PacmanSound::SIREN)
/* omitted */
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&curAudio.pSourceVoice, (WAVEFORMATEX*)&curAudio.wfx)) ) {
return false;
}
if (FAILED(hr = curAudio.pSourceVoice->SubmitSourceBuffer(&buffer) ) ) {
return false; // hr is XAUDIO2_E_INVALID_CALL (=0x88960001)
}
if (FAILED(hr = curAudio.pSourceVoice->Start(0) ) ) {
return false;
}
/* omitted */
}
MSDN says
Returned by XAudio2 for certain API usage errors (invalid calls and so on) that are hard to avoid completely and should be handled by a title at runtime. (API usage errors that are completely avoidable, such as invalid parameters, cause an ASSERT in debug builds and undefined behavior in retail builds, so no error code is defined for them.)
But, I don't know what I'm missing.. Could anyone help me to solve this issue??.
23/01/29 EDIT:
add wavdump.cpp outputs, and LoadSound results.
I believe the problem you are having is that buffer.PlayLength is not valid. You are currently using:
buffer.PlayLength = curAudio.dwChunkSize * playLength;
This means you are setting it in terms of BYTES. Both PlayLength and LoopLength must be in terms of SAMPLES.
First, try using buffer.PlayLength = 0; to verify it works.
Computing sample counts is challenging in a general case, but I have code for it in the DirectX Tool Kit for Audio in SoundEffect.cpp for PCM, ADPCM, xWMA, and XMA2.
For PCM data, you use:
// You'll want to do this with 64-bit integer math to avoid overflow.
samples = (curAudio.dwChunkSize * 8) / (wfx.wBitsPerSample * wfx.nChannels)
My goal is to find what two-byte opcodes generate an illegal instruction exception.
For example, opcodes 0F 0B UD2 raises an invalid opcode exception. The UD2 instruction is provided for software testing to explicitly generate an invalid opcode.
Warning Snake oil code ahead as I'm not familiar with Windows internals.
The code below allocates a 4K page with read/write/execute permissions and using UD2 as a starting point it tries to determine all the possible two-byte opcodes.
First, it copies the two-byte opcodes to the last two bytes of the 4K page
then executes them and checks for the exception code.
I figured that executing the last two page bytes would either
Generate an illegal exception EXCEPTION_ILLEGAL_INSTRUCTION with exactly two bytes.
Generate an access violation EXCEPTION_ACCESS_VIOLATION when extending beyond the 4K page.
Running the code below shows interesting instructions plus many unknowns too:
Illegal opcodes 0x0f 0x0b (error 0xc000001d)
ud2 - Generates an invalid opcode.
Illegal opcodes 0x0f 0x37 (error 0xc000001d)
getsec - Exit authenticated code execution mode.
Illegal opcodes 0x0f 0xaa (error 0xc000001d)
rsm - Resume operation of interrupted program.
Question
The hack'ish code runs fine in this opcode range
Executing opcodes 0x0f 0x0b ... Executing opcodes 0x0f 0xcb
until it encounters these two opcodes
0x0f 0xcc bswap esp
It seems anything that manipulates the stack pointer causes issues whereby it's stuck at this point (clicking Continue just repeats the message)
I've tried moving the opcode execution into its own thread since they have their own stack, but that didn't help!
Is there a way to preserve the stack pointers RSP and RBP or maybe there's a simple fix to resolve it?
(Built using M$ Visual C++ 2019)
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <intrin.h>
// The UD2 (0x0F, 0x0B) instruction is guaranteed to generate an invalid opcode exception.
DWORD InstructionResult;
void ExecuteOpcodes(LPVOID mem)
{
__try
{
// Execute opcodes...
((void(*)())((unsigned char*)mem + 0xFFE))();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
InstructionResult = GetExceptionCode();
}
}
int main()
{
LPVOID mem = VirtualAlloc(NULL, 2, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
DWORD oldProtect = VirtualProtect(mem, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
// Start searching at the UD2 (0x0F, 0x0B) instruction which is guaranteed to generate an invalid opcode exception.
for (int i = 15; i <= 255; i++)
{
for (int j = 11; j <= 255; j++)
{
// Write two byte opcodes at the 4K page end.
*((unsigned char*)mem + 0xFFE) = i;
*((unsigned char*)mem + 0xFFF) = j;
printf("Executing opcodes 0x%02x 0x%02x\n",i,j);
HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ExecuteOpcodes, mem, 0, 0);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
if (InstructionResult == EXCEPTION_ILLEGAL_INSTRUCTION)
{
printf("Illegal opcodes 0x%02x 0x%02x (error 0x%08x)\n", i, j, InstructionResult);
}
}
}
VirtualFree(mem, 0, MEM_RELEASE);
return 0;
}
UPDATE
Based upon the good answer about creating a child process, I've pasted the updated code here: pastebin.com/j3NkL44q
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
// Array of illegal single-byte opcodes
int IllegalSingleByteOpcodes[] = { 0x06 ,0x07, 0x0e, 0x16, 0x17, 0x1e, 0x1f, 0x27, 0x2f, 0x37, 0x3f, 0x60, 0x61, 0xce, 0xd6 };
// Check if a given opcode is illegal
bool is_illegal_opcode(int opcode) {
for (size_t i = 0; i < sizeof(IllegalSingleByteOpcodes) / sizeof(IllegalSingleByteOpcodes[0]); i++) {
if (IllegalSingleByteOpcodes[i] == opcode) {
return true;
}
}
return false;
}
// Create and wait for a process with the given opcodes
void create_and_wait_for_process(int opcode1, int opcode2) {
// Set up startup and process info
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Create command line string
char cmdline[256];
snprintf(cmdline, sizeof(cmdline), "IllegalOpcodes.exe %d %d", opcode1, opcode2);
// Create process
if (!CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
printf("CreateProcess failed (%d).\n", GetLastError());
exit(1);
}
// Wait for process to finish
if (WaitForSingleObject(pi.hProcess, 1000) != WAIT_OBJECT_0) {
TerminateProcess(pi.hProcess, 0);
}
// Clean up handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
int main() {
// Iterate through all possible opcode pairs
for (int opcode1 = 0; opcode1 <= 255; opcode1++) {
// Skip illegal opcodes
if (is_illegal_opcode(opcode1)) {
printf("\nSkipping Illegal Opcode 0x%02x ...\n", opcode1);
continue;
}
printf("\nChecking Opcode 0x%02x ...\n", opcode1);
for (int opcode2 = 0; opcode2 <= 255; opcode2++) {
create_and_wait_for_process(opcode1, opcode2);
}
}
return 0;
}
------------------- IllegalOpcodes.cpp -------------------
#include <windows.h>
#include <stdio.h>
// Offset to write opcodes at the 4K page end
#define CODE_PAGE_END_OFFSET 0xFFE
// Write two byte opcodes at the 4K page end and execute them
void write_and_execute_opcodes(LPVOID code_page_mem, int opcode1, int opcode2)
{
*((unsigned char*)code_page_mem + CODE_PAGE_END_OFFSET) = opcode1;
*((unsigned char*)code_page_mem + CODE_PAGE_END_OFFSET + 1) = opcode2;
__try
{
// Execute opcodes...
((void(*)())((unsigned char*)code_page_mem + CODE_PAGE_END_OFFSET))();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
switch (GetExceptionCode()) {
case EXCEPTION_ILLEGAL_INSTRUCTION:
printf("{0x%02x,0x%02x},", opcode1, opcode2);
break;
default:
// Ignore other exceptions
break;
}
}
}
int main(int argc, char* const argv[])
{
int opcode1 = atoi(argv[1]);
int opcode2 = atoi(argv[2]);
LPVOID code_page_mem = VirtualAlloc(NULL, 2, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
DWORD old_protect = VirtualProtect(code_page_mem, 2, PAGE_EXECUTE_READWRITE, &old_protect);
write_and_execute_opcodes(code_page_mem, opcode1, opcode2);
VirtualFree(code_page_mem, 0, MEM_RELEASE);
return 0;
}
It now includes checks to avoid these single byte illegal opcodes
32-bit Legal
06 push es
07 pop es
0e push cs
16 push ss
17 pop ss
1e push ds
1f pop ds
27 daa
2f das
37 aaa
3f aas
60 pushad
61 popad
ce into
d6 ??? <--- http://ref.x86asm.net/coder64.html#xD6
64-bit Illegal
06 ???
07 ???
0e ???
16 ???
17 ???
1e ???
1f ???
27 ???
2f ???
37 ???
3f ???
60 ???
61 ???
ce ???
d6 ???
maybe there's a simple fix to resolve it?
The UNIX-standard way to resolve this is to do all the test execution in a child process.
When I last worked on Windows 15 years ago, creating a child process was very expensive (slow). But since you have fewer that 64K byte combinations to try, even a slow mechanism will get you all the answers in at most a few hours.
I am trying to perform a system call on 32-bit, but there is an issue.
I originally had a naked function as my stub and used inline assembly, but when I tried to turn it into shellcode, despite it being a 1-to-1 copy of the naked function (When looking at it in Visual Studio's disassembly), it does not function (Access Violation Executing NULL).
It worked perfectly with the naked function, by the way.
Here is the shellcode I wrote:
0: b8 26 00 00 00 mov eax,0x26
5: 64 ff 15 c0 00 00 00 call DWORD PTR fs:0xc0
c: c3 ret
And here is the code: Everything works fine. Memory gets allocated successfully, the problem is whenever I attempt to call NtOpenProcess: it attempts to execute a null pointer, resulting in an access execution violation.
typedef NTSTATUS(NTAPI * f_NtOpenProcess)(PHANDLE, ACCESS_MASK, OBJECT_ATTRIBUTES *, CLIENT_ID *);
INT
main(
VOID
)
{
HANDLE hProcess = NULL;
OBJECT_ATTRIBUTES oaAttributes;
memset(&oaAttributes,
NULL,
sizeof(oaAttributes));
oaAttributes.Length = sizeof(oaAttributes);
CLIENT_ID ciClient;
ciClient.UniqueProcess = GetCurrentProcessId();
ciClient.UniqueThread = NULL;
BYTE Stub[] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0x64, 0xFF, 0x15, 0x0C, 0x00, 0x00, 0x00, 0xC3 };
*(DWORD*)(Stub + 1) = 0x26;
PVOID Mem = VirtualAlloc(NULL, sizeof(Stub), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Mem, &Stub, sizeof(Stub));
DWORD Old = NULL;
VirtualProtect(Mem, sizeof(Stub), PAGE_EXECUTE, &Old);
f_NtOpenProcess NtOpenProcess = (f_NtOpenProcess)Mem;
DWORD Status = NtOpenProcess(&hProcess,
PROCESS_ALL_ACCESS,
&oaAttributes,
&ciClient);
printf("Status: 0x%08X\nHandle: 0x%08X\n",
Status,
hProcess);
getchar();
return NULL;
}
If anyone is wondering why am I doing this, I am really bored and I like to mess around with code when I am :)
As noted by Micheal Petch, the shellcode was wrong.
I only missed one byte (0x0C) that should be 0xC0.
If anyone will ever attempt something so stupid and useless like I did, double-check your shellcode first!
Here is the scoop, I have created a program that sleeps for 30 seconds..Nothing more. Within immunity debugger it looks as such:
0x00061000 >/$ 68 30750000 PUSH 7530; /Timeout = 30000. ms
The code for this program is :
#include <windows.h>
void main()
{
Sleep(30000);
return 0;
}
I am trying to read this line with a C++ program. This is the code for that process:
const char *procName = "blank.exe";
HANDLE hProc = GetProcHandle(procName);
if (hProc == NULL){
cout << "Error Proccess Handle == NULL!!! Can not continue...";
getch();
return 1;
}
//Handle aquired continue.... //
cout << "Handle has been Aquired!\n";
LPVOID RMEM[100];
ReadProcessMemory(hProc, (LPVOID)(0x00061000), &RMEM, sizeof(RMEM), 0);
cout << "Read Memory:" << RMEM;
getch();
return 0;
The problem is every time I run the program I get different results. example 3 results for three runs(00FBFC3C, 0086F9C4, 007CF5EO). I want to be able to read the value of sleep and then after this is perfected I want to overwrite it with a new value like: PUSH EA60; What is going on? I have read the msdn page I have tried looking at the values ReadProMem gives me and there is no such offset in the main module. I'm at a complete loss 0.o
Any help and tips would be amazing.
Most likely the call to ReadProcessMemory is failing. Since you don't check for errors you've no way of knowing that. The documentation says:
Return value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError.
Your error checking might look like this:
if (!ReadProcessMemory(...))
{
DWORD err = GetLastError();
// report error, bail out, etc.
}
My guess is that ReadProcessMemory fails because the address you pass is not valid in the target process. And then when you output RMEM you are merely outputting uninitialized values.
The first step for you is to fix the error handling. Once you've done that you'll know which API call fails and why it fails. Then likely you'll just need to supply a valid address.
Works just fine here, using win7 pro and 32 bit compiled programs. Both programs were built with Mingw and Code::Blocks, the target memory address was determined with the use of OllyDbg.
I can confirm that the delay is successfully changed. That's the purpose of the getchar() - it's to ensure the delay hasn't been started yet when we alter the target program's memory.
1. Target program source - main.c (hackMe.exe)
#include <windows.h>
void main()
{
getchar();
Sleep(30000);
return 0;
}
2. Target area of target program (using ollydbg)
CPU Disasm
Address Hex dump Command Comments
00401334 /$ 8D4C24 04 LEA ECX,[ARG.1]
00401338 |. 83E4 F0 AND ESP,FFFFFFF0 ; DQWORD (16.-byte) stack alignment
0040133B |. FF71 FC PUSH DWORD PTR DS:[ECX-4]
0040133E |. 55 PUSH EBP
0040133F |. 89E5 MOV EBP,ESP
00401341 |. 51 PUSH ECX
00401342 |. 83EC 14 SUB ESP,14
00401345 |. E8 D6050000 CALL 00401920
0040134A |. E8 41080000 CALL <JMP.&msvcrt.getchar> ; [MSVCRT.getchar
0040134F |. C70424 307500 MOV DWORD PTR SS:[LOCAL.7],7530 ; /Time => 30000. ms
00401356 |. E8 8D080000 CALL <JMP.&KERNEL32.Sleep> ; \KERNEL32.Sleep
0040135B |. 83EC 04 SUB ESP,4
0040135E |. 90 NOP
0040135F |. 8B4D FC MOV ECX,DWORD PTR SS:[LOCAL.2]
00401362 |. C9 LEAVE
00401363 |. 8D61 FC LEA ESP,[ECX-4]
00401366 \. C3 RETN
3. Controlling program - main.c
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <tlhelp32.h>
unsigned long GetProcessId( char* szProcName )
{
PROCESSENTRY32 pe32;
HANDLE hHandle;
hHandle = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
pe32.dwSize = sizeof( PROCESSENTRY32 );
if( !Process32First( hHandle, &pe32 ) )
return 0;
while( Process32Next( hHandle, &pe32 ) )
{
if( stricmp( szProcName, pe32.szExeFile ) == 0 )
{
CloseHandle( hHandle );
return pe32.th32ProcessID;
}
}
CloseHandle( hHandle );
return 0;
}
// reads a chunk of memory from a running program's memory space
// Buffer must already be allocaed and hold space for length bytes
BOOL readMemBlock(char *szProgName, unsigned long dwMemAddr, unsigned long length, void *Buffer)
{
HANDLE hHandle;
SYSTEM_INFO sysInfo;
MEMORY_BASIC_INFORMATION mbi;
BOOL resCode;
DWORD lastErrCode;
printf("%s, 0x%x, %d\n", szProgName, dwMemAddr, length);
hHandle = OpenProcess( STANDARD_RIGHTS_REQUIRED|PROCESS_VM_READ, FALSE, GetProcessId( szProgName ) );
if( hHandle == INVALID_HANDLE_VALUE || hHandle == NULL )
{
printf("Error opening process\n");
if (!hHandle)
printf("hHandle == NULL\n");
else
printf("INVALID_HANDLE_VALUE");
return FALSE;
}
resCode = ReadProcessMemory( hHandle, (unsigned long*)dwMemAddr, Buffer, length, NULL );
CloseHandle(hHandle);
return resCode;
}
// reads a chunk of memory from a running program's memory space
// Buffer must already be allocaed and hold space for length bytes
BOOL writeMemBlock(char *szProgName, unsigned long dwMemAddr, unsigned long length, void *Buffer)
{
HANDLE hHandle;
SYSTEM_INFO sysInfo;
MEMORY_BASIC_INFORMATION mbi;
BOOL resCode;
hHandle = OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, GetProcessId( szProgName ) );
if( hHandle == INVALID_HANDLE_VALUE || hHandle == NULL )
return FALSE;
resCode = WriteProcessMemory( hHandle, (unsigned long*)dwMemAddr, Buffer, length, NULL );
CloseHandle(hHandle);
return resCode;
}
int main()
{
unsigned long pId = GetProcessId("hackMe.exe");
if (!pId)
{
printf("Proc handle NOT FOUND\n");
return;
}
printf("Proc handle aquired\n");
unsigned char buffer[50];
readMemBlock("hackMe.exe", 0x401334, 50, buffer); //unsigned long length, void *Buffer)
int i;
for (i=0; i<50; i++)
{
unsigned int curElem = buffer[i];
printf("%02X ", (unsigned int)curElem);
if ((i+1)%16 == 0)
printf("\n");
}
/* output
Proc handle aquired
hackMe.exe, 0x401334, 50
8D 4C 24 04 83 E4 F0 FF 71 FC 55 89 E5 51 83 EC
14 E8 D6 05 00 00 E8 41 08 00 00 C7 04 24 30 75 <-- want these 2 bytes 0x30, 0x75
00 00 E8 8D 08 00 00 83 EC 04 90 8B 4D FC C9 8D
61 FC
*/
// change delay to 2.5 seconds (originally 30 secs)
short newDelayValue = 2500;
writeMemBlock("hackMe.exe", 0x401352, 2, &newDelayValue);
return 0;
}
So i am currently learning about hooking and injection and build a little test application for myself. What i am doing in there is:
Allocating some memory inside the process (using VirtualAllocEx)
Writing code to this code-cave (using WriteProcessMemory)
Hooking a function inside the app to jump to this code-cave
Now i have some problems which i am unsure of why they occur.
My code for creating the cave is this:
DWORD procID = GetCurrentProcessId();
HANDLE procHandle = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, procID);
if (procHandle == NULL) {
std::cout << "OpenProcess failed..." << std::endl;
return -1;
}
DWORD leng = (unsigned int)dynASM_end - (unsigned int)dynASM;
leng += 270;
LPVOID allocAddr = VirtualAllocEx(procHandle, NULL, leng, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
std::cout << "BaseAddr: " << allocAddr << std::endl;
int check = WriteProcessMemory(procHandle, (void*)((char*)allocAddr + 0x10E), dynASM, leng - 270, NULL);
unsigned int bExec = 1;
check = WriteProcessMemory(procHandle, allocAddr, &bExec, sizeof(unsigned int), NULL);
if (check) {
std::cout << "Successfully written asm to process!" << std::endl;
}
Now this is very basic. VirtualAllocEx always returns 0x00030000, is this possible?
This is my simple assembly code:
void testCall()
{
std::cout << "Hook worked!" << std::endl;
}
__declspec(naked) void dynASM()
{
__asm {
push ebp
mov ebp, esp
call testCall
mov esp, ebp
pop ebp
ret
}
}
__declspec(naked) void dynASM_end()
{
}
I believe i can call the function testCall this way, can't i? This is the code which i put into the codecave as you can see above.
Now i have a offset to a function inside another class in which i want to hook. This function only consists of:
__declspec(naked) void Core::HookFunc()
{
__asm {
mov edi, edi
push ebp
mov ebp, esp
mov esp, ebp
pop ebp
ret
}
}
And i try to hook like this:
DWORD hookFuncOffset = 0x14510;
DWORD jumpTo = (DWORD)((char*)allocAddr + 0x10E);
DWORD baseAddr = (DWORD)GetModuleHandle(NULL);
BYTE* finalAddr = (BYTE*)((DWORD)baseAddr + hookFuncOffset);
DWORD oldProtect, bkpProtect, relAdr;
VirtualProtect(finalAddr, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
relAdr = (DWORD)(jumpTo - (DWORD)finalAddr) - 5;
*finalAddr = 0xE9;
*((DWORD*)finalAddr + 0x1) = relAdr;
VirtualProtect(finalAddr, 5, oldProtect, &bkpProtect);
allocAddr is the address for the code-cave i have allocated above.
Now while debugging it i can see that relAddr is 0xFFFABBF9 which seems really high to me. But if i look into the disassembly view of Visual Studio the jmp created leads to 0xF9939B14 which leads to an Access Violation Exception. I would believe that even if the jmp would lead to the "correct" address it would also throw an Exception. What am i doing wrong?