一
前言
二
为什么无法搜索内存改值?
def incr_item(dict, key):
try:
item = dict[key]
except KeyError:
item = 0
dict[key] = item + 1
int
incr_item(PyObject *dict, PyObject *key)
{
/* Objects all initialized to NULL for Py_XDECREF */
PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
int rv = -1; /* Return value initialized to -1 (failure) */item = PyObject_GetItem(dict, key);
if (item == NULL) {
/* Handle KeyError only: */
if (!PyErr_ExceptionMatches(PyExc_KeyError))
goto error;/* Clear the error and use zero: */
PyErr_Clear();
item = PyLong_FromLong(0L);
if (item == NULL)
goto error;
}
const_one = PyLong_FromLong(1L);
if (const_one == NULL)
goto error;incremented_item = PyNumber_Add(item, const_one);
if (incremented_item == NULL)
goto error;if (PyObject_SetItem(dict, key, incremented_item) < 0)
goto error;
rv = 0; /* Success */
/* Continue with cleanup code */error:
/* Cleanup code, shared by success and failure path *//* Use Py_XDECREF() to ignore NULL references */
Py_XDECREF(item);
Py_XDECREF(const_one);
Py_XDECREF(incremented_item);return rv; /* -1 for error, 0 for success */
}
incremented_item = PyNumber_Add(item, const_one);
在获取到item
后,并不会直接对item
内部的内存进行自增,而是调用函数进行加法,创建了一个新的对象incremented_item
,然后再PyObject_SetItem(dict, key, incremented_item)
换回去。所以两个值在内存上并不会是同一个地址。Py_XDECREF(item);
这是减少一次引用计数的意思。python 底层实现里 PyObject 都是有引用计数的。这也意味着我们如果直接修改内存中的值,会同时修改所有使用这个对象的地方。三
通过 hook 解释器底层函数来修改值
PyUnicode_FromString, _PyObject_GenericGetAttrWithDict, PyObject_SetItem, PyObject_Call
。还有一些有用的辅助函数有PyObject_Repr, PyUnicode_AsUTF8, PyGILState_Ensure, PyGILState_Release
Python36-x64\include\Python.h
,虽然不能直接用自己这个 Python 解释器里的函数,但是头文件里很多宏是对对象直接操作的,还是比较有用的。string GetPyObjectString(void* obj)
{
//auto gstate = oPyGILState_Ensure();//auto pyobj = reinterpret_cast<PyObject*>(obj);
auto str_obj = oPyObject_Repr(obj);
if (!str_obj) {
PyErr_Print();
//oPyGILState_Release(gstate);
return {};
}
const char* str = oPyUnicode_AsUTF8(str_obj);
if (!str) {
PyErr_Print();
Py_DECREF(str_obj);
//oPyGILState_Release(gstate);
return {};
}
string ret{ str };
Py_DECREF(str_obj);
//oPyGILState_Release(gstate);
return ret;
}
string obj_class = Py_TYPE(obj)->tp_name;
。同时,尽量减少查看的类型,过滤不感兴趣的调用可以有效减少崩溃。void* My_PyObject_GenericGetAttrWithDict(void* obj, void* name, void* dict) {
static unordered_set<string> intersted_name{
R"#('m_HP')#",
};
ostringstream oss;
auto name_str = GetPyObjectString(name);
if (intersted_name.find(name_str) == intersted_name.end()) {
oss << "Skip: "<<name_str;
LogMsg(oss.str());
return o_PyObject_GenericGetAttrWithDict(obj, name, dict);;
}
string obj_class = Py_TYPE(obj)->tp_name;
oss << "_PyObject_GenericGetAttrWithDict: " << endl
<< '\t' << obj <<":" << obj_class << endl
<< '\t' << name << ":" << name_str << endl
<< '\t' << dict << endl;
// 避免被回收,提前解析字符串
auto ret = o_PyObject_GenericGetAttrWithDict(obj, name, dict);
auto dict_str = GetPyObjectString(ret);
auto t = Py_TYPE(ret);
oss << '\t' << " -> " << ret << " Type: " << t->tp_name << " Value: " << dict_str;
if (obj_class == "Player") {
if (name_str == R"('m_HP')") {
((PyLongObject*)ret)->ob_digit[0] = 78900; // 血量
oss << " hijked -> " << ret << Py_TYPE(ret)->tp_name << " : " << GetPyObjectString(ret);
}
}LogMsg(oss.str());
return ret;
}
((PyLongObject*)ret)->ob_digit[0] = 10000;
设置。四
改值之外?
PyObject_Dir
在嵌入的解释器里查,但是这样逆向写起来实在算不上方便;PyRun_InteractiveLoop
调出交互命令行来,这样应该更加方便。五
结论
看雪ID:mb_fefksfsl
https://bbs.kanxue.com/user-home-979936.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多