Execute binary code

C

citronelu

Is it possible to execute a binary string stored within a python script
as executable code ?

The script is run under Windows, and the binary code (a full executable
file) is stored in a variable in the script.

I know I can use os.system() or os.popen() to run an external file, but
these functions take as argument a string evaluated as command-line.

I also know I could save the binary code as a temporary file, execute
it and delete it afterwards, but this is not an alternative.

Thanks.
 
L

Larry Bates

Is it possible to execute a binary string stored within a python script
as executable code ?

The script is run under Windows, and the binary code (a full executable
file) is stored in a variable in the script.

I know I can use os.system() or os.popen() to run an external file, but
these functions take as argument a string evaluated as command-line.

I also know I could save the binary code as a temporary file, execute
it and delete it afterwards, but this is not an alternative.

Thanks.

What you are asking is a virus/trojan "like" program. There's no reason
you shouldn't be able to write the code to TEMP directory and execute it.

-Larry
 
O

olsongt

Is it possible to execute a binary string stored within a python script
as executable code ?

The script is run under Windows, and the binary code (a full executable
file) is stored in a variable in the script.

I know I can use os.system() or os.popen() to run an external file, but
these functions take as argument a string evaluated as command-line.

I also know I could save the binary code as a temporary file, execute
it and delete it afterwards, but this is not an alternative.

Thanks.

It's not impossible, that's basically what I did on a smaller scale in
pyasm:

http://mysite.verizon.net/olsongt/

A small C-stub executes arbirary asm that was originally built as a
string. The tough part for you would be loading all of the referenced
..dlls into memory and patching in all the relocations from the source
COFF file. It'll be a pain but not entirely impossible.
 
C

citronelu

Larry said:
What you are asking is a virus/trojan "like" program. There's no reason
you shouldn't be able to write the code to TEMP directory and execute it.

-Larry


No, it is not about a trojan, but I guess it's pointless to try to
convince you otherwise.

It's not about being able to write the code to TEMP directory and
execute it, it's about not wanting to do so.

-Cornelius
 
C

Chris Mellon

No, it is not about a trojan, but I guess it's pointless to try to
convince you otherwise.

It's not about being able to write the code to TEMP directory and
execute it, it's about not wanting to do so.

-Cornelius

Writing to a temp file will be at least 3 times as easy and twice as
reliable as any other method you come up with.


<sigh> Repost. Is there any chance at all that ML could set the
reply-to to the list instead of the sender?
 
C

citronelu

Chris said:
Writing to a temp file will be at least 3 times as easy and twice as
reliable as any other method you come up with.

I'm not disputing that, but I want to keep a piece of code (a parser
for Oracle binary dumps, that I didn't wrote) out of foreign hands, as
much as possible. Using a TEMP directory is not "stealth" enough.
 
B

Bjoern Schliessmann

Larry said:
What you are asking is a virus/trojan "like" program.

Why? For being a trojan horse it must fake something. For being a
virus it must replicate itself. Writing an executable doesn't imply
the will to replicate itself.

But you could technically achieve this with standard python too
(just write python source and spawn a python process executing it).

Regards,


Björn
 
C

citronelu

Bjoern said:
But you could technically achieve this with standard python too
(just write python source and spawn a python process executing it).

The code I try to execute is Windows specific and it is binary, not
python. Furthermore, it is stored in a variable within the parent
python script, not stored on harddisk as a file.
 
B

Bjoern Schliessmann

The code I try to execute is Windows specific and it is binary,
not python. Furthermore, it is stored in a variable within the
parent python script, not stored on harddisk as a file.

Sure, I just wanted to show that your special application is not
specific for trojan horses oder viruses. One could achieve similar
replication functionality with python by itself.

Regards,


Björn
 
G

Gabriel Genellina

At said:
I'm not disputing that, but I want to keep a piece of code (a parser
for Oracle binary dumps, that I didn't wrote) out of foreign hands, as
much as possible. Using a TEMP directory is not "stealth" enough.

This is what I would do (untested of course!) (Mostly using the
Win32 API so you'll have to use pywin32 or ctypes).

Call CreateFile with dwShareMode=0, FILE_ATTRIBUTE_TEMPORARY,
FILE_FLAG_NO_BUFFERING, FILE_FLAG_DELETE_ON_CLOSE.
That means that no other process could open the file, if it fits in
available memory probably it won't even be written to disk, and it
will be deleted as soon as it has no more open handles. File name
does not have to end in .exe.
Copy the desired contents into a buffer obtained from VirtualAlloc;
then call WriteFile; release the buffer (rounding size up to next 4KB multiple)
Then CreateProcess with CREATE_SUSPENDED, and CloseHandle on the
file, and CloseHandle on the two handles returned on
PROCESS_INFORMATION. At this stage, the only open handle to the
temporary file is held by the section object inside the process.
Then ResumeThread(hTread) -process begins running- and
WaitForSingleObject(hProcess) -wait until finishes-.
As soon as it finishes execution, the last handle to the file is
closed and it is deleted.

Another approach would be to go below the Windows API and use the
native API function NtCreateProcess -officially undocumented- which
receives a section handle (which does not have to be disk based). But
this interfase is undocumented and known to change between Windows versions...

Or search for a rootkit...


--
Gabriel Genellina
Softlab SRL






__________________________________________________
Preguntá. Respondé. Descubrí.
Todo lo que querías saber, y lo que ni imaginabas,
está en Yahoo! Respuestas (Beta).
¡Probalo ya!
http://www.yahoo.com.ar/respuestas
 
C

Chris Mellon

This is what I would do (untested of course!) (Mostly using the
Win32 API so you'll have to use pywin32 or ctypes).

Call CreateFile with dwShareMode=0, FILE_ATTRIBUTE_TEMPORARY,
FILE_FLAG_NO_BUFFERING, FILE_FLAG_DELETE_ON_CLOSE.
That means that no other process could open the file, if it fits in
available memory probably it won't even be written to disk, and it
will be deleted as soon as it has no more open handles. File name
does not have to end in .exe.
Copy the desired contents into a buffer obtained from VirtualAlloc;
then call WriteFile; release the buffer (rounding size up to next 4KB multiple)
Then CreateProcess with CREATE_SUSPENDED, and CloseHandle on the
file, and CloseHandle on the two handles returned on
PROCESS_INFORMATION. At this stage, the only open handle to the
temporary file is held by the section object inside the process.
Then ResumeThread(hTread) -process begins running- and
WaitForSingleObject(hProcess) -wait until finishes-.
As soon as it finishes execution, the last handle to the file is
closed and it is deleted.

Another approach would be to go below the Windows API and use the
native API function NtCreateProcess -officially undocumented- which
receives a section handle (which does not have to be disk based). But
this interfase is undocumented and known to change between Windows versions...

Or search for a rootkit...

Thats a lot of work to execute a binary image that can be trivially
recovered from the python source with 2 minutes of work (up to 15 if
you have to install Python and google for how to write to a file
first).
 
H

Hendrik van Rooyen

<sigh> Repost. Is there any chance at all that ML could set the
reply-to to the list instead of the sender?

+1

- I regularly hit "reply all", delete the OP, and then I get :

"Message has a suspicious header"

- Hendrik
 
D

Dennis Lee Bieber

The code I try to execute is Windows specific and it is binary, not
python. Furthermore, it is stored in a variable within the parent
python script, not stored on harddisk as a file.

Oh? Then where is the Python script, itself, stored?

What would prevent some astute person from feeding said "variable"
contents to a dis-assembler, then building an actual executable from
feeding the result to an assembler program?

--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
J

Jorgen Grahn

It's not impossible, that's basically what I did on a smaller scale in
pyasm:

For what it's worth[1], under Unix it /is/ impossible. The only way to bring in
new code (short of dynamic libraries) is to call exec(2) or its variations,
and all need a file system object to load the code from.

/Jorgen
[1] Not much to the OP, I'd think.
 
R

richmoore44

Jorgen said:
For what it's worth[1], under Unix it /is/ impossible. The only way to bring in
new code (short of dynamic libraries) is to call exec(2) or its variations,
and all need a file system object to load the code from.

That's totally untrue, how do you think a JIT compiler works? For
example you can generate x86 assembler on the fly and call it using
pyASM see http://mysite.verizon.net/olsongt/usersGuide.html

Cheers

Rich.
 
S

sturlamolden

Jorgen said:
For what it's worth[1], under Unix it /is/ impossible. The only way to bring in
new code (short of dynamic libraries) is to call exec(2) or its variations,
and all need a file system object to load the code from.

The x86 processor cannot tell the difference between code segments and
data segments. If the executable code is stored in string, all you need
is a pointer to the string holding the code. You can cast the string
address to a function pointer (possibly through a void* if the compiler
complains), then dereference (call) the function pointer.

Trojans, viruses and JIT compilers do this all the time. Here is an
(untested) example:

static PyObject*
call_code_in_string(PyObject *self, PyObject *args)
{
char *s;
int size;
int arg1, arg2, arg3;
typedef int (*func_t)(int,int,int);
func_t pfunc;
if(!PyArg_ParseTuple(args, "s#(iii)", &s, &size, &arg1, &arg2,
&arg3)) return NULL;
pfunc = (func_t)((void *)s); /* if it fails, try
memcpy(&pfunc,&s,sizeof(void*)) instead */
return PyInt_FromLong((long)pfunc(arg1, arg2, arg3));
}

Another possibility would be to just return the string address, and
then make the call possibly using ctypes.

static PyObject*
get_string_addr(PyObject *self, PyObject *args)
{
char *s;
int size;
if(!PyArg_ParseTuple(args, "s#", &s, &size)) return NULL;
return PyInt_FromLong((long)((void*)s));
}
 
C

Chris Mellon

Jorgen said:
For what it's worth[1], under Unix it /is/ impossible. The only way to bring in
new code (short of dynamic libraries) is to call exec(2) or its variations,
and all need a file system object to load the code from.

The x86 processor cannot tell the difference between code segments and
data segments. If the executable code is stored in string, all you need
is a pointer to the string holding the code. You can cast the string
address to a function pointer (possibly through a void* if the compiler
complains), then dereference (call) the function pointer.

Trojans, viruses and JIT compilers do this all the time. Here is an
(untested) example:

static PyObject*
call_code_in_string(PyObject *self, PyObject *args)
{
char *s;
int size;
int arg1, arg2, arg3;
typedef int (*func_t)(int,int,int);
func_t pfunc;
if(!PyArg_ParseTuple(args, "s#(iii)", &s, &size, &arg1, &arg2,
&arg3)) return NULL;
pfunc = (func_t)((void *)s); /* if it fails, try
memcpy(&pfunc,&s,sizeof(void*)) instead */
return PyInt_FromLong((long)pfunc(arg1, arg2, arg3));
}

Another possibility would be to just return the string address, and
then make the call possibly using ctypes.

static PyObject*
get_string_addr(PyObject *self, PyObject *args)
{
char *s;
int size;
if(!PyArg_ParseTuple(args, "s#", &s, &size)) return NULL;
return PyInt_FromLong((long)((void*)s));
}

This works fine if the binary data is "pure" asm, but the impresssion
the OP gave is that it's a compiled binary, which you can't just "jump
into" this way.
 
S

sturlamolden

Chris said:
This works fine if the binary data is "pure" asm, but the impresssion
the OP gave is that it's a compiled binary, which you can't just "jump
into" this way.

You may have to offset the function pointer so the entry point becomes
correct.
 
C

Chris Mellon

You may have to offset the function pointer so the entry point becomes
correct.

That won't be enough. You basically would have to re-implement the OS
loading process, handling relocations and loading any linked
libraries. Possible, in theory, but very non-trivial.
 
J

Jorgen Grahn

Jorgen said:
For what it's worth[1], under Unix it /is/ impossible. The only way to bring in
new code (short of dynamic libraries) is to call exec(2) or its variations,
and all need a file system object to load the code from.

The x86 processor cannot tell the difference between code segments and
data segments. If the executable code is stored in string, all you need
is a pointer to the string holding the code. You can cast the string
address to a function pointer (possibly through a void* if the compiler
complains), then dereference (call) the function pointer.

Trojans, viruses and JIT compilers do this all the time. Here is an
(untested) example:
[...]

You probably need to flush the code cache somewhere there, too, don't you?
Or will that resolve itself because that memory area hasn't been executed
before?

I must admit I haven't contemplated this since the MC68000 was state of the
art, before caches became popular.

/Jorgen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top