#esoteric-python
1 messages ยท Page 123 of 1
different exception code
and another one
hrm
how did you get the advanced debug details?
control panel > System and Security > Security and Maintenance > reliablity report
you can see blue-screens through here as well
cool
i wonder what the handling looks like for the oserror tho
that seems strange
definitely surprised me
hmm
more dead python
i wonder if this is a python feature and if its recent
i feel like i remember getting a real "segfault" on windows
but that could have been WSL too
in pycharm you can get process finished with exit code (0xc0000005)
negative a billion and something
i dont use pycharm
graff
why so many application failures
!projects
Kindling Projects
The Kindling projects page on Ned Batchelder's website contains a list of projects and ideas programmers can tackle to build their skills and knowledge.
This channel is for writing terrible Python code. I'd suggest going to one of the help channels #โ๏ฝhow-to-get-help for good Python help.
@rugged sparrow https://www.python.org/dev/peps/pep-0659/ sounds like a lot of bytecode hacks will need to be rewritten to account for this...
assuming theres even a straightforward way to detect quickening
The only way that users will be able to detect the presence of the new interpreter is through timing execution, the use of debugging tools, or measuring memory use.
hope the part aboutthe use of debugging toolsimplies there'll be some implementation details exposed
anyone help me please ```python
e = globals()
p = 95
h = 116
u = 101
y = 108
a = e[chr(u)]
q = 97
l = [p,p,98,117,105,108,h]
l = l + e["".join([chr(i) for i in l]) + "\x69\x6e\x73\x5f\x5f"].list([105,110,115,p,p])
e = e["".join([chr(i) for i in l])]
k = getattr(a[chr(q)][chr(u)],"".join([chr(i) for i in [ 103,u,h,q,h,h,114] ]))
l = [98,68,69,67,79,68,69,70,85,83,67,65,84,79,82,56,49,54,50,51,54,55,56,48,57]
a[e.chr(97)]["".join([chr(i) for i in (l + [54])])] = a["k"]
a["".join([chr(i) for i in (a["l"] + [57,49,53])])] = a[chr(u)]
l = str()
bDECODEFUSCATOR81623678096(bDECODEFUSCATOR8162367809915,"".join(chr(i) for i in [112,114]) + "".join(chr(i) for i in ([105]+ [110,116])) )((chr(a[e.chr(97)]["y"]) + chr(int("1"*3)) + chr(a[chr(121)] )))
idk how this works
my friend sent me
it prints lol
Anything in particular? Because I mean, it's really just a big mess that can't exactly be explained well
but like
i like how there is pp
how can i make it into readable form
You don't
Or rather, shouldn't
No matter what you do here it's still going to be a mess
Well actually
heh
At first glance it looks like malware because malware uses methods like that to hide that it's really malware lmao
print("lol") technically
Make it into readable form print("lol")
Jack this is literally the second time in a row
lol
You're stealing my thunder here 
!e
e = globals()
p = 95
h = 116
u = 101
y = 108
a = e[chr(u)]
q = 97
l = [p,p,98,117,105,108,h]
l = l + e["".join([chr(i) for i in l]) + "\x69\x6e\x73\x5f\x5f"].list([105,110,115,p,p])
e = e["".join([chr(i) for i in l])]
k = getattr(a[chr(q)][chr(u)],"".join([chr(i) for i in [ 103,u,h,q,h,h,114] ]))
l = [98,68,69,67,79,68,69,70,85,83,67,65,84,79,82,56,49,54,50,51,54,55,56,48,57]
a[e.chr(97)]["".join([chr(i) for i in (l + [54])])] = a["k"]
a["".join([chr(i) for i in (a["l"] + [57,49,53])])] = a[chr(u)]
l = str()
bDECODEFUSCATOR81623678096(bDECODEFUSCATOR8162367809915,"".join(chr(i) for i in [112,114]) + "".join(chr(i) for i in ([105]+ [110,116])) )((chr(a[e.chr(97)]["y"]) + chr(int("1"*3)) + chr(a[chr(121)] )))
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
lol
You can make anything into a mess if you try hard enough
uhmm
You don't need an obfuscator, just a need to torture yourself
Pin that sentence
๐ญ
i was meant to say
:lol:
but my nitro ran off
Take for example
!e
(lambda _, __, ___, ____, _____, ______, _______, ________:
getattr(
__import__(True.__class__.__name__[_] + [].__class__.__name__[__]),
().__class__.__eq__.__class__.__name__[:__] +
().__iter__().__class__.__name__[_____:________]
)(
_, (lambda _, __, ___: _(_, __, ___))(
lambda _, __, ___:
chr(___ % __) + _(_, __, ___ // __) if ___ else
(lambda: _).func_code.co_lnotab,
_ << ________,
(((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __)
- _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ <<
__) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______
<< ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) <<
((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) <<
__) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______
<< (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) +
_) << ((((___ << __) + _) << _))) + (((_______ << __) - _) <<
(((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ <<
_))) + (_____ << ______) + (_ << ___)
)
)
)(
*(lambda _, __, ___: _(_, __, ___))(
(lambda _, __, ___:
[__(___[(lambda: _).func_code.co_nlocals])] +
_(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else []
),
lambda _: _.func_code.co_argcount,
(
lambda _: _,
lambda _, __: _,
lambda _, __, ___: _,
lambda _, __, ___, ____: _,
lambda _, __, ___, ____, _____: _,
lambda _, __, ___, ____, _____, ______: _,
lambda _, __, ___, ____, _____, ______, _______: _,
lambda _, __, ___, ____, _____, ______, _______, ________: _
)
)
)
@gritty mesa :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 25, in <module>
003 | File "<string>", line 25, in <lambda>
004 | File "<string>", line 27, in <lambda>
005 | AttributeError: 'function' object has no attribute 'func_code'
nice
lol
so he did this all manually?
like my friend
There's a 10000 lines code to just print hello world
Yep
he said it's an obfuscator but alr i see i see
omg
so hes lying
That's possible
I was watching the bot logs since we just merged a change to the help channels
But unlikely
and I see this pop up
lmao
!e
class _(metaclass=(type("__", (type,), {"__init__": lambda cls, name, bases, dct: print(f"""a{setattr(cls, '__del__', lambda self: print(f"hello world {type(self)()}"[0:11]))}{cls()}"""[0].replace('a', ''), end="")}))):...
@gritty mesa :x: Your eval job has completed with return code 143 (SIGTERM).
001 | hello world
002 | hello world
003 | hello world
004 | hello world
005 | hello world
006 | hello world
007 | hello world
008 | hello world
009 | hello world
010 | hello world
011 | hello world
... (truncated - too many lines)
Full output: too long to upload
enjoy
o lol
!e
from __future__ import annotations
@lambda c:c()
class __annotations__:
def __setitem__(self, name, value):
globals()[value] = globals()[name]
class Number:
def __init__(self, value):
self.value = value
def __pos__(self):
self.value += 0.5
return self
def __str__(self):
return str(self.value)
let: i = Number(5)
++i
print(i)
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
6.0
This is the channel where nightmares come from
do you know a way to do it like him?
like try to understand how it works
!e
if 1+1-1+1-1+1-1+1-1+1-1+1-1+1-1+1-1+1-1+1 : print("Hello world")
@acoustic gust :white_check_mark: Your eval job has completed with return code 0.
Hello world
I mean, you basically just try your hardest to add as many unnecessary steps as possible
And make it an absolute mess
One sec lemme get an example
class While:
def __init__(self, condition, code):
self.condition = condition
self.code = code
def __del__(self):
if eval(self.condition):
exec(self.code)
globals().update(locals())
type(self)(self.condition, self.code)
x = 3
While("x < 50", "x += 3")
print(x)
```For example, this is already cursed, but somewhat readable
its because its not visalbe
so how does it prints then?
e = globals()
p = 95
h = 116
u = 101
y = 108
a = e[chr(u)]
q = 97
l = [p,p,98,117,105,108,h]
l = l + e["".join([chr(i) for i in l]) + "\x69\x6e\x73\x5f\x5f"].list([105,110,115,p,p])
e = e["".join([chr(i) for i in l])]
k = getattr(a[chr(q)][chr(u)],"".join([chr(i) for i in [ 103,u,h,q,h,h,114] ]))
l = [98,68,69,67,79,68,69,70,85,83,67,65,84,79,82,56,49,54,50,51,54,55,56,48,57]
a[e.chr(97)]["".join([chr(i) for i in (l + [54])])] = a["k"]
a["".join([chr(i) for i in (a["l"] + [57,49,53])])] = a[chr(u)]
l = str()
getattr(builtins, "print")('lol')
class While:
___ = lambda self: ((exec(self.__), globals().update(locals()),
type(self)(self.___, self.__)
) if eval(self.___) else ...)
def __init__(self, __, ___):
self.___ = __; self.__ = ___
((_ := globals()['__builtins__'].setattr), (
x := getattr(self.__class__,
(_(self.__class__, (t := "__del__"), lambda ________: (...,...)),
t)[1]), _(x, "__code__", While.___.__code__)))
i changed the last line into what it really is
ohh i see
getattr(builtins, "print")('lol') -> print('lol')
thanks
bDECODEFUSCATOR81623678096(bDECODEFUSCATOR8162367809915,"".join(chr(i) for i in [112,114]) + "".join(chr(i) for i in ([105]+ [110,116])) )((chr(a[e.chr(97)]["y"]) + chr(int("1"*3)) + chr(a[chr(121)] ))) is just convoluted for the sake of being so
it does not take many seconds to decode
yes
i dont really understnad why is that so
like
i dont see the declaration of it
maybe it's hidden somewhere?
It is
oh
thats the hidden part of it
hmm
a["".join([chr(i) for i in (a["l"] + [57,49,53])])] = a[chr(u)] seems to be doign that
oh?
Defining the variables that is, and the line above that
a = e[chr(u)]
chr(u) -> chr(101) -> e -> the globals object
oh
So he's writing to globals
uhmm so that's a way to register a variable
!e
globals()["var"] = 5
print(var)
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
5
yep
do you know how this work? ```python
"".join([chr(i) for i in (a["l"] + [57,49,53])])
it's kinda
messy i think
It's just making a bunch of characters
uhmm
l = [97,97,98,117,105,108,116]
print("".join([chr(i) for i in (l + [57,49,53])]))
!e
l = [97,97,98,117,105,108,116]
print("".join([chr(i) for i in (l + [57,49,53])]))
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
aabuilt915
oh?
Ah wait it's re-defined
!e
l = [98,68,69,67,79,68,69,70,85,83,67,65,84,79,82,56,49,54,50,51,54,55,56,48,57]
print("".join([chr(i) for i in (l + [57,49,53])]))
@gritty mesa :white_check_mark: Your eval job has completed with return code 0.
bDECODEFUSCATOR8162367809915
So there's that
ohh
idk how did u even know how to do this
lol
but really cool
!e ```python
e = globals()
p = 95
h = 116
u = 101
y = 108
a = e[chr(u)]
q = 97
l = [p,p,98,117,105,108,h]
l = l + e["".join([chr(i) for i in l]) + "\x69\x6e\x73\x5f\x5f"].list([105,110,115,p,p])
e = e["".join([chr(i) for i in l])]
k = getattr(a[chr(q)][chr(u)],"".join([chr(i) for i in [ 103,u,h,q,h,h,114] ]))
l = [98,68,69,67,79,68,69,70,85,83,67,65,84,79,82,56,49,54,50,51,54,55,56,48,57]
a[e.chr(97)]["".join([chr(i) for i in (l + [54])])] = a["k"]
a["".join([chr(i) for i in (a["l"] + [57,49,53])])] = a[chr(u)]
l = str()
bDECODEFUSCATOR81623678096(bDECODEFUSCATOR8162367809915,"".join(chr(i) for i in [112,114]) + "".join(chr(i) for i in ([105]+ [110,116])) )((chr(a[e.chr(97)]["y"]) + chr(int("1"*3)) + chr(a[chr(121)] )))
@crystal urchin :white_check_mark: Your eval job has completed with return code 0.
lol
wow it actually works
we use some repl tricks to figure it out or we use python debugging. or both i guess ๐
lol
Here's a funny thing I discovered
You can use multiple nots in a statement
It's hard to make code blocks on mobile but
if not not not not not not not True:
print("something")```
Pretty good for obfuscating
!e
from typing import Callable, Union
class if_:
def __init__(self, condition: bool):
self.condition = condition
self.done = False
def do(self, action: Callable):
if self.condition:
self.done = True
action()
class elif_:
def __init__(self, parent: Union[if_, 'elif_'], condition: bool):
self.parent = parent
self.condition = condition
def do(self, action: Callable):
if self.condition and not self.parent.condition and not self.parent.done:
self.parent.done = True
action()
class else_:
def __init__(self, parent: if_):
self.parent = parent
def do(self, action: Callable):
if not self.parent.condition and not self.parent.done:
self.parent.done = True
action()
i = 7
a = if_((i == 5))
a.do(lambda: print("equals five"))
b = elif_(a, (i < 3))
b.do(lambda: print("is less than three"))
b = elif_(a, (i > 7))
b.do(lambda: print("is over seven"))
else_(a).do(lambda: print("didn't pass"))
@outer dust :white_check_mark: Your eval job has completed with return code 0.
didn't pass
Ehh
hello
in Smalltalk, booleans are objects that have methods like ifThen and there's no if statement in the language, so it's not that far off
print( "".join( [chr(i) for i in [105, 32, 108, 111, 118, 101, 32, 121, 111, 117]]))```
lol
!e ```python
print( "".join( [chr(i) for i in [105, 32, 108, 111, 118, 101, 32, 121, 111, 117]]))
@sick hound :white_check_mark: Your eval job has completed with return code 0.
i love you
yea me too
not not x is also faster than bool(x)
oh cool
This channel is nightmare fuel.
How can I learn the ctypes nightmare fuel?
experiment with stuff
Any advice on what to experiment with or where?
start with some of the code you see in here
and try to mess with it
and ask questions
alright I'll search for ctypes
!e
import ctypes;ctypes.string_at(0)
interesting
!e
__import__('ctypes').string_at(5000)
!e
__import__('ctypes').string_at(id(1))
Interesting!
!e
print(__import__('ctypes').string_at(id(1)))
@golden finch :white_check_mark: Your eval job has completed with return code 0.
b'x'
Okay - where does the x come from?
Or am I just reading random chunks of memory?
Furthermore, it gives different strings on different machines!
!e
print([__import__('ctypes').string_at(id(i))for i in range(10)])
@golden finch :white_check_mark: Your eval job has completed with return code 0.
[b'\xf9', b'v', b'Z', b'!', b'/', b' ', b'\x0f', b'\x0e', b'\x1f', b'\r']
I think I'm reading random memory
this could be a good rng
@rugged sparrow What does any of this do?
You're reading some weird stuff in memory
I was under the impression it should be reading 1
Or is it reading a PyObjectsomething
not random, the address at id(i) is the start of the memory for the int object i.
!e ```py
import ctypes
import sys
some_string_that_is_going_to_be_in_memory = "string"
size = sys.getsizeof(some_string_that_is_going_to_be_in_memory)
original_spot = id(some_string_that_is_going_to_be_in_memory)
length = len(some_string_that_is_going_to_be_in_memory)
for i in range(original_spot+size, original_spot+size-length, -1):
print(ctypes.string_at(i-2), hex(i-2))```
!e
import ctypes
for i in range(10):
array = (ctypes.c_byte * i.__sizeof__()).from_address(id(i))
print(list(bytes(array))[-4:])
@woven bridge :white_check_mark: Your eval job has completed with return code 0.
001 | [0, 0, 0, 0]
002 | [1, 0, 0, 0]
003 | [2, 0, 0, 0]
004 | [3, 0, 0, 0]
005 | [4, 0, 0, 0]
006 | [5, 0, 0, 0]
007 | [6, 0, 0, 0]
008 | [7, 0, 0, 0]
009 | [8, 0, 0, 0]
010 | [9, 0, 0, 0]
@tribal moon :white_check_mark: Your eval job has completed with return code 0.
001 | b'g' 0x7f8fc0633f25
002 | b'ng' 0x7f8fc0633f24
003 | b'ing' 0x7f8fc0633f23
004 | b'ring' 0x7f8fc0633f22
005 | b'tring' 0x7f8fc0633f21
006 | b'string' 0x7f8fc0633f20
ive been busy with other things but its still in the works. i think it's definitely doable with a smart enough injector like gdb but dunno if itll be possibleworthwhile to do it with ctypes alone.
and it would likely break for any new python version since the injection addresses would likely need to be hard coded
I still think it can be done with just ctypes
right, think we had this discussion before
possible for sure but perhaps difficult and brittle beyond the point of being worthwhile
What did you learn though?
like overall? hmm.. learned more about the internal structure of the cpython parser, a good bit about injection, how it's done on different OS's, learned about some interesting pyhon injection projects like pyrasite. also had never heard of code caves before.
i mean i'm still going though, but dont hold your breath.
what is esoteric python
Golfing, Python VM languages, obfuscation, code gore and other general Python weirdness
(lambda: "esoteric python")()
What exactly is code gore?
really ugly code
ugly in being hard to read or just a mess but somehow works?
hard to read and ugly
for example look at JSON source code: ```py
#json.py
if (not skipkeys and ensure_ascii and
check_circular and allow_nan and
cls is None and indent is None and separators is None and
default is None and not sort_keys and not kw):
iterable = _default_encoder.iterencode(obj)
else:
if cls is None:
cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators,
default=default, sort_keys=sort_keys, **kw).iterencode(obj)```
The names are too long, that's what makes pep8 compliant code ugly.
As a mathematician, i like my variables are single letter, and following conventions for single letter variables
Have you ever wondered why only mathematicians read mathematical research?
Cos most current education systems are a joke, except where the mathematicians study
ยฏ\_(ใ)_/ยฏ
I'm still trying to find the integral of x^x
Ooh I got it, you gotta speak mathematics to read mathematics, and in order to speak mathematics you need to study mathematics
And in order to study mathematics, you need to write a lot of equations, and that's more paper friendly with single symbol operations and references
I use python like a mathematical language
It has the right notation for sets, it sorta has matrices
Tuple pairs
Arithmetic
Integrals and other symbolic mathematics takes a bit more work
trivia: what will cause a fatal error?
1: while True: time.sleep(1)
2: def foo(): while True: print('hi') threading. Thread(target=foo).start() foo()
3: print('idk')
Pls code block the code
1: ```py
while True:
print('hi')
2: ```py
def foo():
while True:
print('hi')
threading.Thread(target=foo).start()
foo
3: ```py
print('idk')
what will cause fatal error
And neither, if you want a fatal error you do this:
from threading import Thread as T
def morte():
try:morte()
except BaseException:morte()
while 1:T(target=morte, daemon=True).start()
lel
): formatting is bad
What do you mean?
!e And if you want to fatally not raise an error on Windows,
Death = lambda*a:type(*a)()
class Obitus(metaclass=Death):
def __del__(self):
try:[Death(self) for _ in range(13)]
except:self.__class__()
print("memento mori")
@floral meteor :x: Your eval job timed out or ran out of memory.
memento mori
bro why does this server have 3 PATRON ROLES? !!?
!e ```py
for i in range(40):
print('a' * i)
!e ```py
for i in range(100000):
print('a' * i)
Because we have 4 different levels (3 of which give a role) https://www.patreon.com/python_discord
!e ```py
for i in range(100):
print('a' * i)
thank you
a mountain of a
Try obfuscating the process
!e
for i in range(100):
print('a' * i-1+2-3+2-1+3-2)
!e ```py
for i in range(100):
print('a' * (i-1+2-3+2-1+3-2))
what you trying to do?
!e
for i in range(100):
print('a' * (i-1+2-3+2-1+3-2))
@heavy ermine :white_check_mark: Your eval job has completed with return code 0.
001 |
002 | a
003 | aa
004 | aaa
005 | aaaa
006 | aaaaa
007 | aaaaaa
008 | aaaaaaa
009 | aaaaaaaa
010 | aaaaaaaaa
011 | aaaaaaaaaa
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/mayeganaya.txt?noredirect
a mountain if a
!e ```py
def p(a, *b):
if b:
h = b.pop(0)
if b:x, y, (z,) = a, h, b
else:x,(y,),z = a,b,1
else:x,y,z = 0,a,1
while x < y:
yield x
x += z
def q(a): return'a' *a
r=(1<<7)-28
for i in p(r):
print (q(i))
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 |
002 | a
003 | aa
004 | aaa
005 | aaaa
006 | aaaaa
007 | aaaaaa
008 | aaaaaaa
009 | aaaaaaaa
010 | aaaaaaaaa
011 | aaaaaaaaaa
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/uboyevibis.txt?noredirect
It needs to be valid python syntax
Newlines between statements
Or run it through a proper onelinerizor
!e
from threading import Thread as T
def morte():
try:morte()
except BaseException:morte()
while 1:T(target=morte, daemon=True).start()
@gusty island :x: Your eval job has completed with return code 139 (SIGSEGV).
001 | Fatal Python error: _Py_CheckRecursiveCall: Cannot recover from stack overflow.
002 | Python runtime state: initialized
003 |
004 | Current thread 0x00007facc5eb8700 (most recent call first):
005 | File "<string>", line 3 in morte
006 | File "<string>", line 3 in morte
007 | File "<string>", line 3 in morte
008 | File "<string>", line 3 in morte
009 | File "<string>", line 3 in morte
010 | File "<string>", line 3 in morte
011 | File "<string>", line 3 in morte
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/vebufehuye.txt?noredirect
!e```
!e
Death = lambda*a:type(*a)()
class Obitus(metaclass=Death):
def __del__(self):
try:[Death(self) for _ in range(13)]
except:self.__class__()
print("memento mori")
@gusty island :x: Your eval job timed out or ran out of memory.
memento mori
TIL
not too surprizing
what does this mean?
Try running it on windows
it'll take about 10 minutes
depending on the system
something small will blue screen
your graphics drivers are usually the first to go
not recommended on your personal or work pc
completely useless on Unix systems
it's a singleton. None does the same
I'm still working on a kernel-panic-induction-protocol as a Linux version of blue-screen-induction-protocol
it's a bit harder to whack linux, as unlike windows it's actually an alright OS
huh. can i create singletons or are they specially handled by the compiler interpreter?
!e print((lambda i,c:c({~0XBis(0o2)&0xFfFfor(0b01, ):-0.9j,0:i@9.7e+1} [0.e+0jand(0o00)])+c({~0XBis(0o2)&0xFfFfor(0b01, ):-0.9j,0:i@9.7e+1} [0.e+0jand(0o00)] -(0x02<<0x04))+c(~(~((0x42<<i@42.e-24) &{.2:0b0is{0.e-1j:...}}[2e-1]) <<( 0x06&0o06)>>1)*3 +0b101)+c(~(~((0x42<<i@42.e-24) &{.2:0b0is{0.e-1j:...}}[2e-1]) <<( 0x06&0o06)>>1)*3 +0b101 -(0x02<<0x04)))(type("",(object,),{"__matmul__":lambda _,__:int(__)})(),chr)) make a full on obfuscator with this design
@tribal moon :white_check_mark: Your eval job has completed with return code 0.
001 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
003 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
004 | <string>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
005 | aAbB
ignore the stupid warnings
christ
sure you can make them, but enforcing the use may not be possible
Hey @slim sonnet!
It looks like you tried to attach a Python file - please use a code-pasting service such as https://paste.pythondiscord.com
!e
for i in range(100):
print('a' * (i-1+2-3+2-1+3-2))
@heavy ermine :white_check_mark: Your eval job has completed with return code 0.
001 |
002 | a
003 | aa
004 | aaa
005 | aaaa
006 | aaaaa
007 | aaaaaa
008 | aaaaaaa
009 | aaaaaaaa
010 | aaaaaaaaa
011 | aaaaaaaaaa
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/ucosilaror.txt?noredirect
!e
for i in range(1000):
print('a' * (i-1+2-3+2-1+3-2))
@heavy ermine :white_check_mark: Your eval job has completed with return code 0.
001 |
002 | a
003 | aa
004 | aaa
005 | aaaa
006 | aaaaa
007 | aaaaaa
008 | aaaaaaa
009 | aaaaaaaa
010 | aaaaaaaaa
011 | aaaaaaaaaa
... (truncated - too many lines)
Full output: too long to upload
!e
for i in range(999):
print('a' * (i-1+2-3+2-1+3-2))
@heavy ermine :white_check_mark: Your eval job has completed with return code 0.
001 |
002 | a
003 | aa
004 | aaa
005 | aaaa
006 | aaaaa
007 | aaaaaa
008 | aaaaaaa
009 | aaaaaaaa
010 | aaaaaaaaa
011 | aaaaaaaaaa
... (truncated - too many lines)
Full output: too long to upload
!e
globals()[''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x5f', 16), int('0x5f', 16), int('0x62', 16), int('0x75', 16), int('0x69', 16), int('0x6c', 16), int('0x74', 16), int('0x69', 16), int('0x6e', 16), int('0x73', 16), int('0x5f', 16), int('0x5f', 16)]])].__dict__[''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x70', 16), int('0x72', 16), int('0x69', 16), int('0x6e', 16), int('0x74', 16)]])](''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x68', 16), int('0x65', 16), int('0x6c', 16), int('0x6c', 16), int('0x6f', 16), int('0x20', 16), int('0x77', 16), int('0x6f', 16), int('0x72', 16), int('0x6c', 16), int('0x64', 16)]]))
@slim sonnet :white_check_mark: Your eval job has completed with return code 0.
hello world
!e
getattr(globals()[''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x5f', 16), int('0x5f', 16), int('0x62', 16), int('0x75', 16), int('0x69', 16), int('0x6c', 16), int('0x74', 16), int('0x69', 16), int('0x6e', 16), int('0x73', 16), int('0x5f', 16), int('0x5f', 16)]])].__dict__[''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x70', 16), int('0x72', 16), int('0x69', 16), int('0x6e', 16), int('0x74', 16)]])], ''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x5f', 16), int('0x5f', 16), int('0x63', 16), int('0x61', 16), int('0x6c', 16), int('0x6c', 16), int('0x5f', 16), int('0x5f', 16)]]))(getattr(getattr(__import__, ''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x5f', 16), int('0x5f', 16), int('0x63', 16), int('0x61', 16), int('0x6c', 16), int('0x6c', 16), int('0x5f', 16), int('0x5f', 16)]]))(''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x73', 16), int('0x79', 16), int('0x73', 16)]])), ''.join([chr(x) for x in []]).join([chr(x) for x in [int('0x70', 16), int('0x61', 16), int('0x74', 16), int('0x68', 16)]])))
@slim sonnet :white_check_mark: Your eval job has completed with return code 0.
['', '/snekbox/user_base/lib/python3.9/site-packages', '/usr/local/lib/python39.zip', '/usr/local/lib/python3.9', '/usr/local/lib/python3.9/lib-dynload']
An elegant solution for printing the path
https://pastebin.com/Bxrs2dnQ
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
hastebin wouldn't fit it
how so?
and how is it done for Ellipsis? or is it an interpreter / runtime env thing
!e print(....new(type(...))is...)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
True
returns the already existing...
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | None
002 | <class 'NoneType'> Ellipsis
!e ```py
class Cell:
_reg = {}
_PyObject = import('ctypes').py_object
def new(cls, victim):
if id(victim)in cls._reg:return cls._reg[id(victim)]
else:return super().new(cls)
def init(self, prisoner):
type(self)._reg |= {id(prisoner):self}
self.cell_contents = prisoner
self._content_type = self._PyObject.from_address(id(prisoner)+8).value
def hack(self, new:type):
victim = self._PyObject.from_address(id(self.cell_contents)+8)
old, victim.value = victim.value, new
return old
def del(self):
self.hack(self._content_type)
del self._reg[id(self.cell_contents)]
def repr(self):
return f"<cell({self.cell_contents!r}) at {hex(id(self.cell_contents))}>"
def hash(self):return id(self.cell_contents)
EllipsisType = Cell(...).hack(type(None))
print(...)
NoneType = Cell(...).hack(EllipsisType)
print(NoneType, ...)
print(Cell(...) is Cell(...), Cell(True)is Cell(False))
T, O = Cell(True), Cell(1)
O.hack(T.hack(int))
if 1: print(1 == True, 1)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
001 | None
002 | <class 'NoneType'> Ellipsis
003 | True False
004 | 1 False
e! import subprocess
~
e!
bashCommand = "ls"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

attempting to break the bot is allowed in #bot-commands
not here
here we break python itself
!e ```py
_0='echo 69';
from subprocess import Popen as _1;
from subprocess import PIPE as _2;
_3 = _1(_0, stdout=_2);
_4 = _3.communicate()
print(*_4)
It doesn't allow that
stripped terminal?
Can't access the terminal in any way I think
!e ```py
_0='/usr/local/lib/python3.9';
from subprocess import Popen as _1;
from subprocess import PIPE as _2;
_3 = _1(_0, stdout=_2);
_4 = _3.communicate()
print(*_4)
@floral meteor :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | File "/usr/local/lib/python3.9/subprocess.py", line 951, in __init__
004 | self._execute_child(args, executable, preexec_fn, close_fds,
005 | File "/usr/local/lib/python3.9/subprocess.py", line 1821, in _execute_child
006 | raise child_exception_type(errno_num, err_msg, err_filename)
007 | PermissionError: [Errno 13] Permission denied: '/usr/local/lib/python3.9'
!e
import subprocess
subprocess.call('python')
alright then
im going to use that to obfuscate something aha
!e
import subprocess
subprocess.call('python')
@floral meteor :warning: Your eval job has completed with return code 139 (SIGSEGV).
[No output]
!e
!e ```py
from signal import*
from os import abort
for i in range(2,22):
try:signal(i, lambda*a:print(*a)or 0)
except Exception:pass
abort()
@floral meteor :warning: Your eval job timed out or ran out of memory.
[No output]
!e ```py
(s := lambda: import('sys').setrecursionlimit(2**24) or s())()
@dawn kayak :warning: Your eval job has completed with return code 139 (SIGSEGV).
[No output]
!e
import weakref
ref = None
class Target:
def __del__(self):
global ref
ref = weakref.ref(self)
def g():
w = Target()
w = None
print(ref)
g()
@gusty island :white_check_mark: Your eval job has completed with return code 0.
<weakref at 0x7f7195d8e3b0; dead>
that's a large id
I'm still trying to makes sense of the opcodes for just an annotation
!e not e!
okay the LOAD_CONST is actually two lines after
damn stack based bytecode
@gusty island :warning: Your eval job has completed with return code 0.
[No output]
!e ```py
def Mortis():
try:Decessus
except:Mortis()
Mortis()
@floral meteor :x: Your eval job has completed with return code 139 (SIGSEGV).
001 | Fatal Python error: _Py_CheckRecursiveCall: Cannot recover from stack overflow.
002 | Python runtime state: initialized
003 |
004 | Current thread 0x00007f90cfc28740 (most recent call first):
005 | File "<string>", line 2 in Mortis
006 | File "<string>", line 3 in Mortis
007 | File "<string>", line 3 in Mortis
008 | File "<string>", line 3 in Mortis
009 | File "<string>", line 3 in Mortis
010 | File "<string>", line 3 in Mortis
011 | File "<string>", line 3 in Mortis
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/jepijomizo.txt?noredirect
that's how you death it
@floral meteor would there be a way to lock a resource that the python interpreter needs to access and then kill the program with the lock still in place?
!e
import os
os.system('ping kekex.cc')
@gusty island :warning: Your eval job has completed with return code 0.
[No output]
facts, bro - a,b,c,x,y,z,i,j,(k)
!e
import os
while True:
os.system('ping kekex.cc')
@gusty island :warning: Your eval job timed out or ran out of memory.
[No output]
oh lord what is this
the box is sealed
๐ญ
It was made by those who are Dead, and the Dead keep it, until the time comes. The box is sealed.
there must be a way
!e
import os
while True:
os.system('ping 27.0.0.1')
There is no known way. The boxes have been exhaustively tested.
not gonna work - it has no network connection
I've had some experience breaking into and out of boxes before. I did not succeed.
I learnt that generally the only way to get data in is via the way it inputs code, and the only way out is how it outputs output.
I haven't looked into snekbox, but I'm guessing it's a virtual machine.
well, I guess I will continue hacking the box before I make another atempt
suggestion: track network traffic and see if anything goes in/out
it has no network
you also cannot start new processes (so os.system fails immediately)
no sockets no os.system?
correct
well, kinda makes sense to disable those
You can't escape the isolated environment or do anything really bad
I think Google made it and tested it
thonk
there must be a better way
I really doubt there's a way out.
There's always a way out
Yes, there is a way out. However, it probably involves incredible amounts of voodoo and access to the source of the box and the host os.
Read: Unless you're a nation-state, it's impossible
Even if you root the box you'd need an 0day to get out of the vm
that's my point
wait, if the box is rooted would it still filter ingoing/outgoing net requests/
my guess is yes
I don't doubt you. How is input/output sent?
could you explain?
anyone know how to use __build_class__?
let's say 13 hours
okay - that's about the same time as tomorrow for me
What are the dunders:
__class__
__base__
__subclasses__
__name__
__module__
__builtins__
?
Specifically, the code is this:
(([x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__))['__import__']('os').system('ls -la')
I can't find documentation on object
seems to just list subclasses or smth
so then we get a catch_warnings thing
looks like a "missing __builtins__" escape
fetches the catch_warnings object, recovers __builtins__ from its _module, then does standard sandbox escape stuff
so just type('',(),{})?
No.
Or am I misunderstanding ancestor?
That would create a subclass of object.
yeah
the asterisk intrigues me
You can manually alter the mro and __class__
mro? that's method resolution order, but I don't know what that means
and class is just what it was an instance of, right?
yes and yes
so what does mro do?
method resolution order
the order that methods are resolved
do you have a quick demo?
bases are those things in class x(bases): which it inherits from right
no I don't have a memo I'd like to post something else in fact but it's a bit inconvenient right now
but I'm guessing resolve has a definition I don't know
Unrelated, here's a challenge:
It's possible to perform a sandbox escape with the following conditions:
- Your code will be passed to
execwith__builtins__set to{} - Your code cannot contain the
"or'characters
What's the shortest possible program to spawn a shell and executels? (Assuming the latest 3.9 version)
just use [*map(chr,[array of ints])]
Map and chr are missing with no builtins.
oh
oh
that is not fun
!e
print((1).__class__.__base__.__subclasses__())
!e
print(len((1).__class__.__base__.__subclasses__()))
@golden finch :white_check_mark: Your eval job has completed with return code 0.
114
hmm
Yeah that's pretty much as expected
I wonder if it's possible to force obtaining the str class through other means (||repr(), doc||, etc))
Another idea would be to restrict what kinds of literals are allowed :>
like, ellipsis only
I'm sure you could enforce that, too
parse it with ast
just visiting the ast nodes- yeah
_del = delattr
_imp = __import__
for i in dir(__builtins__):
_del(__builtins__, i)
sys = _imp('sys')
del _del, _imp
sys.meta_path.clear()
sys.modules.clear()``` write some code to recover from this
How can I change readonly attributes? I'm sick of Python telling me I can't break things
you didn't delete sys - you can use that to find the builtins
what is the ().class.base.subclasses())[84] on your machine?
_frozen_importlib.BuiltinImporter
this is impressive algorithmically too
>>> s_assign('a',0,'b')
>>> x='a'
>>> x
'b'
however it opens the path for significantly more voodoo than I expected
strings use different internal layouts depending on their contents. e.g. 'uh oh: \U0002a6a5'
>>> s_assign('foo',0,'bar')
>>> 'foo'
'foo'
>>> x='foo'
>>> x
'foo'
:(
How can I change builtin methods of int as a class?
forbiddenfruit module
so how does it work?
oh lord
bit too much
what if I want to write my own?
Go for it
I wrote fishhook the same way
But fishhook can handle exceptions in hooks while forbiddenfruit crashes
alr
Feel free to ping me with questions
I have no idea where to start @rugged sparrow .
So generate_slotmap does the dynamic work of figuring out where each dunders slot is
Start there
okay
also
[exec('from '+i+' import *',globals())for i in[*__import__('sys').modules]]
for no reason
oh lord it's all one file
@rugged sparrow what's a slotmap?
It's a mapping that says where different slot pointers need to go
Slot pointers are where c functions for Dunders go
What's a mapping, and how can you make a dunder point to a c function?
oh wait
Is mapping similar to in math?
Oh it's just a dictionary, dunder names to locations
locations being memory addresses of c functions?
The way that fishhook works is it takes the slot functions that exist on normal python classes and sticks them into builtin classes
Well data that can be used calculate the memory address
Yea
All they do is look up the corresponding python function and call it
So by placing them in a builtin class, you can just place a python function in the class dictionary and it works
so if I have the class
class foo:
def bar(self,x):
return x+1
what is the slotmap?
@rugged sparrow ?
The slot map isn't dependent on the class
It's based on how type objects look in memory
type objects?
@rugged sparrow ?
Do you mean subclasses of type?
or things like int, str, etc (are those the same thing though)
So all classes are type objects (including type itself)
okay
so type objects are things those metaclass is type?
do you have an example slotmap?
could you paste it?
(furthermore, how do you access it?)
@rugged sparrow you there?
You can import generate_slotmap from fishhook and call it
alright time to learn a bit more c
structs are just grouped variables, right?
like "classes" without methods
It's a set of values arranged specifically in memory
it's a variable that holds several data items?
It's a thing that holds some variables in some part in memory that you have to set later on, and all those variables can be accessed through the name of the structure variable you defined
alright how do you set it later on?
You define it, and then set it by name, you could also set it like an array too
Do you have an example?
struct Points {
int x;
int y;
}
struct Points points;
points.x = 4;
points.y = 3;``` something like that
Okay - looks a lot like a methodless class.
in Python there's also ctypes.Structure that you can use
and I think it works if you pass it into an actual function that requires a struct
struct Points points = {4, 3};```
you could also initialize it all to 0 by just putting a 0 inside the curly braces
Yes
any restrictions?
No
Base specifies the offset of the struct on the type object struct, secondary specifies the location of the slot pointer on that base struct
the what
offset being distance in memory
code examples might be useful if you have some
?
@rugged sparrow
Yea offset is distance in memory
Sorry I don't haven't code examples rn I don't have my computer
Read up on a stackoverflow article or something it explains it really well
things are usually referenced by an offset
Like for example local variable and function arguments are referenced by offset
they're referenced relative to the base pointer
Yes
So base offset is?
The base pointer + offset is an address inside the struct of a specific value
I'm not sure what you're referencing
my c knowledge is - variables and structs exist, and pointers
code examples would really help if you could give any
Okay wait just a second
if you have something like ```c
#include <stdio.h>
struct Points {
int x;
int y;
};
int main() {
struct Points points;
points.x = 4;
points.y = 3;
printf("Address of `points`: %p, size of `points`: %d, value of `points`: %d\n", &points, sizeof(points), points);
printf("Address of `points.x` %p, size of `points.x`: %d, value of `points.x`: %d\n", &points.x, sizeof(points.x), points.x);
printf("Address of `points.x` %p, size of `points.x`: %d, value of `points.y`: %d\n", &points.y, sizeof(points.y), points.y);
}and run this this will output:
Address of points: 000000000061FE18, size of points: 8, value of points: 4
Address of points.x 000000000061FE18, size of points.x: 4, value of points.x: 4
Address of points.x 000000000061FE1C, size of points.x: 4, value of points.y: 3``` notice the addresses? See how the address of points is 0x000000000061FE18, and the value of it itself is x, that's because it points to the first starting value of the struct, where x is stored. Since x is an int, the size of it is 4 bytes, so if you wanted to access y for example, you'd go to base address which is 0x000000000061FE18, add the offset to it 000000000061FE1C (0x000000000061FE18 + 0x4) and then deference it to get the actual value. This is a really bad and rough explanation cause I'm eating right now but I'm sure you can find a better one on youtube or something
reading now
@rugged sparrow slotmaps?
later...
Slotmap is just a term I coined for fishhook, because the dictionary is a mapping of the slot pointers
i'm gonna be honest i don't fully know what i was on about
i THINK
i was referring to how, using the gc module, you can get all the referrers to an object
referrers? things that reference an object, right?
yeah
so if you and your buddy share a secret object (or constant, since the parser makes all literal constants in the same block of code the same)
okay
then you can use gc to find your buddy and then call methods on it
that was probably it
so hypothetically if you write two things that use the literal constant 12353823672
in the same source file
one can find the other
(assuming nothing has changed about the cpython's treatment of literal constants since the original post)
yeah
my knowledge of gc is rusty
i once did some fucked up shit where the gc module formed part of my program's logic
nobody told me weak references existed
so i like
made them from scratch
weak references?
well now I want to see
yeah, uh, things don't get cleaned up until their reference count drops to zero OR they're in a cycle that can't be accessed from globals()/locals()/etc scopes
okay
so what if you want to hold a reference to something, but you want it to get deallocated if you're the only thing holding a reference
you can make a "weak reference" that doesn't count
Okay - why would you need that?
i was implementing something SORT OF like a language
and that language needed something like a garbage collector
rebuilding a lang inside python?
but why should i implement a garbage collector when python can garbage collect for me
so by using weakrefs when appropriate python can clean up some of my messes automatically
but yeah i can't think of a good reason to use them except "i need python to free up some resources for performance reasons but i also need to know if it's still getting used"
print_exception threw an exception and crashed
the cache died with it
print_exception somehow crashed again
and again
and then when calling exit() exit crashed
and then it segfaulted
we need a thread for spicy errors
this is art - but I reckon you should communicate through integers in range(-5,255) to make it truly esoteric
I use [*{*some_massive_string}] and pop a character when it's needed
I knew I wasn't the only one to use [*{*}]!
What do you use it for?
Importless random
But it's like a runtime hash
It's random between runtimes, it's consistent in the same code block
I guess you could probably find the seed or figure out the seeding algorithm using this
Myself, I read id() or random memory
id(1) also works
But the nested stars is a convenient, short, obfuscated shuffle
But some days I prefer to boogie rather than shuffle ;)
I am looking for a way to remove every attribute from new classes, because that's funny IG. I thought about using type.__prepare__ = lambda *_: {} but you can't assign to type. Any idea?
how evil of you? how evil of you that you separate attribute from their classes? what happens to their feelings?
offended feelings intensify
__builtins__.__build_class__ = ... could be an approach
@thin trout :white_check_mark: Your eval job has completed with return code 0.
001 | __build_class__(func, name, /, *bases, [metaclass], **kwds) -> class
002 |
003 | Internal helper function used by the class statement.
@thin trout :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 3, in <module>
003 | File "/usr/local/lib/python3.9/dis.py", line 85, in dis
004 | raise TypeError("don't know how to disassemble %s objects" %
005 | TypeError: don't know how to disassemble builtin_function_or_method objects
iirc it basically takes care of setting up a class' namespace by executing its body as a function with the locals set to the class' __dict__
the approach could then be this: you implement a new build class and pass an empty function instead of the actual function thats supposed to be used for its body
!e
build_class = __builtins__.__build_class__
def new_build_class(func, *args, **kwargs):
return build_class(lambda *args, **kwargs: None, *args, **kwargs)
__builtins__.__build_class__ = new_build_class
class A:
a = 1
print(A().a)
@woven bridge :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 13, in <module>
003 | AttributeError: 'A' object has no attribute 'a'
@thin trout
Haha niiiice
I can assign to type.
Scroll up and see either my Cell class or my hack class to reassign the class position in memory.
Don't forget to subclass the original value at the memory, which means actually fetch it first using hack(type).value
That's funny! I'm working on this right now
It seems fairly simple
the basic idea is
old=__builtins__.__build_class__
__builtins__.__build_class__=lambda args:(out:=o(args),type(something with out))
Interesting, is it using ctypes?
@thin trout
this might work for some arcane reason
__builtins__.__build_class__=lambda*a,**b:type(a[1],(),{})
That's interesting too!
ah, not yet
just need to make it support kwargs
>>> __builtins__.__build_class__=lambda*a,**b:type(a[1],(),{})
>>> class a(int,foo='bar'):
n=2
def f(self,x):
print(x)
return 3
>>> a.n
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
a.n
AttributeError: type object 'a' has no attribute 'n'
>>> a.f(1,2)
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
a.f(1,2)
AttributeError: type object 'a' has no attribute 'f'
yeah it works
sometimes the solution without ctypes is the good solution
@thin trout
Noice
.bm yeet the class attribute
o=__builtins__.__build_class__;__builtins__.__build_class__=lambda f,n,*bases,**kwargs:(globals().update(_=o(f,n,*bases)),type(n,bases,{i:getattr(_,i)if i[:2]=='__'else lambda*_:exec((lambda:0).__code__.replace(co_consts=()))for i in dir(_)}))[1]
class a(int,foo='bar'):
def f(self,x):
print(x)
return 3
print('test')
a.f(1,2)
stdout:
test
[SIGSEGV]
slightly more complex example
Is there a #esoteric-python role?
for notification purposes?
Nah we don't have any
I guess a certificate of some mental illness could work
Definitely not going insane after reading this channel too much
What is this code doing though haha
No, just as a general certificate of insanity using this channel.
segfaults when any non-dunder methods of a class are called
@golden finch do you know why it segfaults?
It's the ```py
exec((lambda:0).code.replace(co_consts=()))
generally, CodeObject manipulation results in an easy segfault
Yea but you should try to figure out why that segfaults
It tries to get an element from co_consts that doesn't exist
which I presume is the source of the segfault
I stole it from codegolf stack exchange
Hey @thorny shoal!
It looks like you tried to attach a Python file - please use a code-pasting service such as https://paste.pythondiscord.com
Yea but why does that cause a segfault? (This is a cool thing to look at because you find a way to something really cool)
o=__builtins__.__build_class__
__builtins__.__build_class__=lambda a,b,*c,**d:(_:=o(a,b,*c,**d),type(b,c,{i:getattr(_,i)for i in dir(_)}|_._))[1]
I'm liking this new trick
I don't know where the source for evaluating CodeObjects is.
(I haven't looked into the cpython source)
it's massive
What exactly is this trick?
On class creation, it looks for the attribute _, and if so, combines it with the class' attributes on creation
i.e
!e
o=__builtins__.__build_class__
__builtins__.__build_class__=lambda a,b,*c,**d:(_:=o(a,b,*c,**d),type(b,c,{i:getattr(_,i)for i in dir(_)}|_._))[1]
class foo:
def __init__(self,value):
self.value=value
_={'__repr__':lambda self:self.value}
x=foo('potato')
print(x)
@golden finch :white_check_mark: Your eval job has completed with return code 0.
potato
it's purely to make your code harder to debug
I am making the father of all undebuggable programs
huh thats actually really cool
I might make it segfault if you try to declare another class without it
no idea where to look in ceval.c
it's big
Do a find in file for TARGET(LOAD_CONST
TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
DISPATCH();
}
I've been reading that bit
and looking at defs
Yea now look at GETITEM
#ifndef Py_DEBUG
#define GETITEM(v, i) PyTuple_GET_ITEM((PyTupleObject *)(v), (i))
#else
#define GETITEM(v, i) PyTuple_GetItem((v), (i))
#endif
so
#define GETITEM(v, i) PyTuple_GetItem((v), (i))
now to PyTuple_GetItem
where is that defined?
some other file
Nope, Py_DEBUG isn't defined
So you're looking for PyTuple_GET_ITEM
can't find the definition either
Include/cpython/tupleobject.h line 23
#define PyTuple_GET_ITEM(op, i) (_PyTuple_CAST(op)->ob_item[i])```
Include/cpython/tupleobject.h line 23
#define PyTuple_GET_ITEM(op, i) (_PyTuple_CAST(op)->ob_item[i])```
that was almost simultaneous
So now we know what LOAD_CONST actually runs on the C side
It's indexing the tuples array
is a tuple an array internally?
It has a an array inside Its struct
okay
But the important thing is that there isn't any bounds checking happening
like checking that i is in a valid range?
Yes
mhm
That's why it segfaults
Because it's trying to load whatever memory is there as a python object
It's possible to make sure the memory it tries to load is a valid python object
how?
By altering the argument to LOAD_CONST
I challenge you to try to figure out how to do that
Yes
I can change the argument, sure
You can change the argument to a max of 255 (there are ways to get larger but I'll explain that later)
I have no clue what to do, but I can change the argument
I can show you an example implementation with comments
(lambda:0).__code__.replace(co_code='\x64\0x01')
for example
Would love that
no clue how to implement it myself
Download my implementation and mess around with it
Yes
Once you have figured out how this works, LOAD_FAST has the same kind of bug, try to implement this there
(within the context of python's memory?)
Yea the os limits us to pythons address space
just change the 0x64 to 0x7c, right?
Not quite, LOAD_FAST Is related to a different object, not the consts tuple
So look at how LOAD_FAST is implemented and try to reason what object you need to work with
TARGET(LOAD_FAST): {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
format_exc_check_arg(tstate, PyExc_UnboundLocalError,
UNBOUNDLOCAL_ERROR_MSG,
PyTuple_GetItem(co->co_localsplusnames,
oparg));
goto error;
}
Py_INCREF(value);
PUSH(value);
DISPATCH();
}
uh
ยฏ_(ใ)_/ยฏ
PyObject **localsplus = _PyFrame_GetLocalsArray(frame);
We need to know where that array comes from so we get the right offset (i) from it
@golden finch I gtg for a bit, but try to figure out where localsplus is stored (and what the struct of that object looks like)
okay
I just wanna say you people are geniuses
holy cow youre new?!
yeah I've only been here for maybe a week and a half
you have more messages than me in this server
geez although im guessing you had pretty good knowledge of lower level stuff even before you joined
well i help alot in the help channels
All I knew was a little bit of bytecode
I don't think anyone who spends time in #esoteric-python should touch the help channels
My bad - I've been here for ~28 days
and now I'm lost to the dark side
o=__builtins__.__build_class__
__builtins__.__build_class__=lambda a,b,*c,**d:(_:=o(a,b,*c,**d),type(b,c,{i:getattr(_,i)if'_'in dir(_)else exec((lambda:0).__code__.replace(co_consts=()))for i in dir(_)}|_._))[1]
updated slightly
Well i mean its not all that bad at least you'll know how to help with errors and complex solutions
although the massive amount of insanity may be a bit overwhelming for the people wanting help
o=__builtins__.__build_class__
__builtins__.__build_class__=lambda a,b,*c,**d:(_:=o(a,b,*c,**d),type(b,c,{i:getattr(_,i)if'_'in dir(_)and type(_._)==dict else exec((lambda:0).__code__.replace(co_consts=()))for i in dir(_)}|_._))[1]
yeah just ping us if any of the help channels ask for something truly awful ig
How did you start learning this stuff if you dont mind me asking?
I really just fiddled with dir() until I found __code__, then I read the documentation and some blogs on it, and I arrived here
yeah @rugged sparrow can't find it
localsplus can be found on frameobjects
where?
https://github.com/python/cpython/blob/main/Include/frameobject.h
not seeing them?
where?
Make sure you're looking at 3.9
I have no clue
I can help in an hour
okay
@rugged sparrow
sorry i got swept into looking at 3.10 optimizations
the struct definition is in that file you sent a link for
but im still looking at 3.10, prob all night
i can help tomorrow
later then
hi
hi
Hello @inner vector Just wondering why you have posted 'hi' in a dozen different channels?
I wan to send 50 messages
Ah. Please don't spam to get there.
@inner vector Why not chat in #python-discussion or #ot0-psvmโs-eternal-disapproval?
oh
If you join VC, people do read the messages in the associated text channels.
@rugged sparrow you free now?
I'm about to go to bed
damn
I'm not free, I am enslaved by the need to torture python until it reveals all it's secrets, and 2+2 evaluates to 5
didn't we do this last week?
>>> ctypes.cast(id(4),ctypes.POINTER(ctypes.c_int))[6]=5
>>> 2+2
5
Make it a bit more inherently evaluate to 5 rather than a substitution
Make python love big brother, not just pretend to
What do you mean then? Change int.__add__ somehow?
Gimme a sec I'm on phone
.
Hmm
!e ```py
class Cell:
_reg = {}
_PyObject = import('ctypes').py_object
def new(cls, victim):
if id(victim)in cls._reg:return cls._reg[id(victim)]
else:return super().new(cls)
def init(self, prisoner):
type(self)._reg |= {id(prisoner):self}
self.cell_contents = prisoner
self._content_type = self._PyObject.from_address(id(prisoner)+8).value
def hack(self, new:type):
victim = self._PyObject.from_address(id(self.cell_contents)+8)
old, victim.value = victim.value, new
return old
def del(self):
self.hack(self._content_type)
del self._reg[id(self.cell_contents)]
def repr(self):
return f"<cell({self.cell_contents!r}) at {hex(id(self.cell_contents))}>"
def hash(self):return id(self.cell_contents)
class BigBrother(int):
def add(self, other):
a=super().add
return a(a(other))-1
two = 2
Cell(two).hack(BigBrother)
print ( two + two )
Cell(two).hack(int)
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
5
what the hell
so confused what that even does
also why do you name your variables like so
Probably 1984.
True, but I don't recall 'hack' being mentioned anywhere in 1984. I've got a copy on my shelf, and I'm planning on rereading it, but I don't think I'll do it by hand.
Yeah, the string hack doesn't occur anywhere in the book.
challenge:
#your code here
try:
raise
except:
pass
else:
print('Foo')
Make this code print Foo, by putting something at the top. The last line must be executed.
I'm not very good at ctypes, but the RuntimeError from having nothing to raise might be editable, or something could use the exception to hook into frames.
i wonder why bare excepts are okay but bare raises arent
they are in the right context
!e
try:
raise Exception()
except Exception:
raise # propagates the exception
@woven bridge :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 2, in <module>
003 | Exception
but why?
sometimes you might need to do a little work after an exception occurs without making any prescriptions about what should be done with the exception
so the responsibility is passed on through propagation
!e
import dis
import sys
import ctypes
def mutable_bytes(instance: bytes):
return memoryview(
(ctypes.c_uint8 * len(instance)).from_address(
id(instance) + bytes.__basicsize__ - 1
)
).cast('B')
code = mutable_bytes(sys._getframe().f_code.co_code)
for i in range(0, len(code), 2):
instruction = code[i]
if instruction == dis.opmap['RAISE_VARARGS']:
code[i] = dis.opmap['NOP']
try:
raise
except:
pass
else:
print('Foo')
@woven bridge :white_check_mark: Your eval job has completed with return code 0.
Foo
wow
consider me impressed
that's exactly the sort of voodoo I was looking for
how does it work?
(like, you're getting the frame and editing the codeObject, but how does mutable_bytes work?)
accesses the bytecode for the module frame and rewrites the instruction for the raise to one that does nothing, aka a no op
oh, mutable_bytes
- is taking the memory address of the
bytesobject, adding the boilerplate size for the bytes struct to it to get to the content memoryid(instance) + bytes.__basicsize__ - 1 - using this new address to create an array of uint8 (unsigned bytes)
- wrapping it up in a memoryview for some extra functionality e.g. being able to slice the memory without copying
- and finally casting the memoryview to
B(unsigned 8bit int with no prescribed endianness) because otherwise you might get complaints regarding endianness when trying to assign values to its indexes.
format characters can be seen here https://docs.python.org/3/library/struct.html#format-characters
@sick hound :white_check_mark: Your eval job has completed with return code 0.
Foo
you could mess that up a lot more
bit by bit:
what does (ctypes.c_uint8 * len(instance)) do?
okay
that's horrible syntax
list or array?
c_uint8 by itself represent the type for an individual c_uint8, whereas c_uint8 * i returns another type which represents a fixed size c_uint8 array of length i.
the right hand side is supposed to be an int, e.g. len(code) not code itself.
yes, I got that
!e
import ctypes
print(ctypes.c_uint8 * 20)
@woven bridge :white_check_mark: Your eval job has completed with return code 0.
<class '__main__.c_ubyte_Array_20'>
so it's actually an array not a list
its a class that is used to represent a fixed size c array
ok
so now what happens if you call that class's from_address on a... byteobject's memory location?
from_address is a classmethod, so it returns an instance of the class which, using usual subscript syntax, can be used to access individual items of the array starting from the given address. the class also implements the buffer protocol which is why it can be passed into memoryview.
!e
import ctypes
data = b'\x01\x02\x03\04'
array_1 = (ctypes.c_uint8 * len(data)).from_address(id(data) + bytes.__basicsize__ -1)
print(array_1[0])
print(bytes(array_1))
array_2 = (ctypes.c_uint16 * (len(data)//2)).from_address(id(data) + bytes.__basicsize__ -1)
print(array_2[0])
print(bytes(array_2))
@woven bridge :white_check_mark: Your eval job has completed with return code 0.
001 | 1
002 | b'\x01\x02\x03\x04'
003 | 513
004 | b'\x01\x02\x03\x04'
ok, so here you can see the kind of abstraction these types provide when dereferencing memory. while both arrays are using the exact same data underneath, one dereferences to 8 bit ints, and the other one as 16 bit ints.
and another thing is, since theyre both sharing the same underlying memory, changes in one will apply to the other
!e
import ctypes
data = b'\x01\x02\x03\04'
array_1 = (ctypes.c_uint8 * len(data)).from_address(id(data) + bytes.__basicsize__ -1)
array_2 = (ctypes.c_uint16 * (len(data)//2)).from_address(id(data) + bytes.__basicsize__ -1)
print(data)
print(array_2[0])
array_1[0] = 255
print(data)
print(array_2[0])
array_2[0] = 0
print(data)
print(array_1[1])
@woven bridge :white_check_mark: Your eval job has completed with return code 0.
001 | b'\x01\x02\x03\x04'
002 | 513
003 | b'\xff\x02\x03\x04'
004 | 767
005 | b'\x00\x00\x03\x04'
006 | 0
it's bit more involved than that
theyre closely related so its a description of both
The basic size includes the fields in the instance declared by the macro PyObject_HEAD or PyObject_VAR_HEAD (whichever is used to declare the instance struct) and this in turn includes the _ob_prev and _ob_next fields if they are present.
to try and summarize, the object's struct has some fields at the start that are always there in every instance (though strings are an exception to this). basicsize is the combined size of these always present fields
the stuff afterwards is the variable length data of the instance, each item of which takes up itemsize bytes of space
hi
๐ hey
people when they find this
i might be wrong since im very new to this but it seems to be so they dont actually have to call the class
nevermind, i get it now
so it basically creates the class
then initiates a class object
and discards the old class
@lambda _:_()
class A:
pass
```is equivalent to```py
class A:
pass
A = A()
yes
and of course, since it's esoteric, they have to use the underscore variable ๐
Speaking of that - https://repl.it doesn't support decorators such as @lambda _:_)( - you've got to do ```py
c=lambda :()
class A:
pass
oh wow, didn't know that!
"it works on my machine"
it's a python version thing. decorator syntax was relaxed significantly in 3.9. replit only uses 3.8 at the latest iirc
if you try to use a lambda decorator in any python version below 3.9 it wont work, replit or not.
!pep 614
huh - thanks for the @eval("lambda _:_()") loophole!
I used @getattr(0, '', lambda x:x())
wait so why does it discard the class?
oh wait nvm i see now
what would be an actual use of this tho?
Do you mean decorators, lambdas as decorators, or that particular lambda as a decorator?
that one in particular
If you want to make a particular object, and don't care about needing its class again, then you might define the class and use the decorator on it.
is there a way you can use the object tho from that?
The object gets stored in a variable with the name of the class.
!e ```py
@lambda :()
class x:
def add(self, other):
print(other)
x + "Hello, world!"
@snow beacon :white_check_mark: Your eval job has completed with return code 0.
Hello, world!
!e i usually end up trying to make a mutable int by using
n:0 = [0]
def mutate(a):
n [0] += a
mutate(4)
print (n [0])
@floral meteor :white_check_mark: Your eval job has completed with return code 0.
4
That's not very object-oriented.
I end up making a Cell that once it holds a value, it has heightened mutability
@woven bridge Any discoveries yet?
so i know that in memory an object is an array of memory starting at its id.
So far all I know about it is the 8th position after that id is a reference to it's class.
What are the other values?
i.e. 2, 4, 16, 24, 32, etc...
The first 8 bytes are the reference count of the object
Second set is the class
After that depends on the object
!e
!eval [code]
Can also use: e
*Run Python code and get the results.
This command supports multiple lines of code, including code wrapped inside a formatted code
block. Code can be re-evaluated by editing the original message within 10 seconds and
clicking the reaction that subsequently appears.
We've done our best to make this sandboxed, but do let us know if you manage to find an
issue with it!*
!e
import os
print(os.getcwd())
@unique heath :white_check_mark: Your eval job has completed with return code 0.
/snekbox
yes that windbg is frustrating to use
SOME_CONSTANT = int(''.join(['0b',*{*'0123456789abcdef'*32}]),16)
a great start for a script, i guess?
howd you get it to randomize like that?
Sets have random-ish order. the *{*...} unpacks the string into a set, then unpacks the set into the list containing it.