[ANY] Work with memory in SourcePawn
Introduction
Hello, everyone. Not so long ago I decied to make my own Team Fortress 2 server, and it took me to write SourcePawn plugins. I started study this language relatively recent and generally I like it, except for some things that I would like to talk about, and also discuss solutions for it. If you don't want to read philosophical reasoning about language limitations, I recommend you to skip this section and get straight to the next one. One of those things - process address space access restriction. Unfortunately, but we can work only with server and engine libraries. Well, normally, this should be enough, because main server engine work occurring it is in these libraries. However, don't take me wrong, but I don't like fell myself limited when working with memory, heh. Nevertheless, you could deal with this... I thought so first time. But what I really didn't like, is inability to "mix" memories of plugin and server. In other words, if I want to insert pointer on any memory region of my plugin in any library of process or the srcds itself - I'll be forced to abstain, because first of all, I can't get native pointers of variables or functions of my plugin, and secondly, I can interract with, again, server and engine libraries. Even if I had ability to get variables addresses via "#emit", I would get address relative special plugin memory, but relative process memory (nullptr). Moreover, you can't allocate memory in srcds process. Again, don't take me wrong! I'm not criticizing SourcePawn language with the suggestion that language is necessary to be improvements. SourcePawn is not the language, which is designed for such complex memory manipulations. It is designed to create simple plugins, which will change or add server functionality. In general, it's good language to create server-side modifications. However, personally I don't like these restrictions I have enumerated above, and therefore I would like to propose some solutions for servers on Windows platform. I'm pretty sure someone says that such functionality can be implemented with native functions, and I'll be forced to agreed. With the help of native you can implement anything, however, I prefer to not use external libraries which create dependence of plugin from SourceMod modules. Therefore, I will reproduce all my solutions with the pure SourcePawn code, which can be easily used as include file. Let's begin. What do we have? We can use special thing called "gamedata" to find code pattern and then patch it. Basically, it's file, which consists code pattern, module name, operating system, and offset. It's a quite useful thing. To work with native process memory (not plugin) we have two main functions - StoreToAddress() and LoadFromAddress(). These functions allow us to access memory to the desired address, but since we don't have full information about memory regions (we can receive addresses only via gamedata search), we are blind. Another main feature of SourcePawn language - SDKCall() function, which was developed for calling native class methods (such as CBaseEntity). That's all. These things are going to allow us to make SourcePawn much more powerful in terms of working with memory. Getting Windows version First, we start with the simplest - let's try to get the version of our operating system. How we can do this without using native? Very simple! Watch my hands. In Windows, there is a special place in the memory, which address is static since the days of Windows 2000. This place is a KUSER_SHARED_DATA structure, which contains the information that is necessary for us. The address of this structure is 0x7FFE0000. Let's use the available information to write a function for obtaining the OS version. PHP Code:
Getting PEB Process Environment Block, perhaps, is one of the most useful and necessary things for us. Using it, we can access the list of loaded modules, as well as the address of the srcds process itself. There is only one "but" - it can not be obtained with LoadFromAddress(), since it's address changes from start to start of the program. Moreover, we can get it only by using the x86 assembler. Here's how it looks in C++: PHP Code:
First of all, we need to create a file in the gamedata folder, called "any.tutor.memory.txt": PHP Code:
Let's implement the necessary functional. To begin with, we write the code: PHP Code:
Then write the functions of reading the data at the address: PHP Code:
After these functions, create two more: PHP Code:
And now we are going to implement the really necessary functionality: PHP Code:
Now let's write a fairly specific function: PHP Code:
And now we will write something really impressive: PHP Code:
First, the function checks whether the PEB has already been found, and returns its address if it was. Then the address of the server module is obtained. We use a small trick here, based on the architecture of PE files, namely the rule that the first two bytes of exe or dll are equal to the "MZ" constant. When we start the search, the SourceMod core immediately stumbles upon these two bytes and returns the address of the beginning of the module. After all this, the CreateMemoryForSDKCall() function is called. Its goal is to find a unique pattern in the unused area of the address space of the server module, which will be found by the function PrepSDKCall_SetSignature() and FindPattern() later. Then we build an SDK call, which is not really an SDK, but our future function, written in x86 assembler. PrepSDKCall_SetSignature() simply finds our unique pattern, in which we will be able to write any code in the future. Then we use FindPattern() to find the same place and change it, implementing our assembler code there. After all this, we call the SDKCall() function and get our PEB address. Cunningly, is not it? Now, with PEB on hand, we can do so many interesting things. Let's try to get the srcds address, for example. PHP Code:
Conclusion The article turned out to be quite big as for me, so I think that is enough for now. We examined the implementation of obtaining a version of Windows and the ability to execute its own x86 assembler code. As you can see, this is quite possible. If you like this topic, then I will continue writing these kind of tutorials, in which I will consider even more interesting things. Thank you for reading. |
Re: [ANY] Work with memory in SourcePawn
Very nice! Thanks for this!!
|
Re: [ANY] Work with memory in SourcePawn
Nice, thank you for this.
It is useful. I personally would like to see more examples :D |
Re: [ANY] Work with memory in SourcePawn
Looks well, but I'm not sure if you can call functions in buffer for data. Does SDKCall() already call VirtualProtect() with execute flag? What about Linux? Probably we can call kernel with int 80h. This makes me think you can exploit/hack servers/OS only with embedding shellcode in plugin.
|
Re: [ANY] Work with memory in SourcePawn
SDKCall definitely isn't going to set memory executable, but at least some of a plugin's memory is necessarily going to be. Perhaps it all is, but I didn't check the VM code. I assume the example in the first post works, which would indicate it is (whether intentional or not).
|
Re: [ANY] Work with memory in SourcePawn
Quote:
Quote:
Quote:
|
Re: [ANY] Work with memory in SourcePawn
Thanks, on Windows to get that server.dll address what I looked.
|
All times are GMT -4. The time now is 13:52. |
Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.