杂项 FPS FPS逆向 ZEROKO14 2023-11-24 2024-01-01 fps辅助整体分析
FPS游戏特性导致的,需要非常高的实时性,不能过度的网络验证
绘制部分可以使用透明窗口覆盖在游戏上,非常难以检测
自瞄部分,捕捉准星数据,准星路径曲线不规则或有中断(不能平滑,人类操作鼠标非常不圆滑)
连续爆头的行为检测行为数据异常等等
相关基础 角度计算
tan,cos,sin函数,参数为弧度 ,弧度为π表示180度。
atan2,acos2,asin2,返回值为弧度 。
$$ 角度=(弧度*180)/π $$
$$ 弧度=(角度*π)/180 $$
了解线性代数:
向量内积是一个向量在另一个向量上的投影长度乘以另一个向量的长度
矩阵的乘法:
快捷键写法 1 2 3 4 if (GetKeyState(VK_F1)&1 ){ }
绘制的三种方法
hook d3d/opengl
游戏窗口上自行绘制
自己创建窗口,在自己创建的窗口上绘制
3维坐标转屏幕算法 两种坐标求导算法
不需要矩阵 $$ 摄像机到准星的距离分辨率=\frac{\frac{屏幕水平分辨率}{2}}{\tan{\frac{水平FOV}{2}}} $$ $$ \tan{x差角度}=\frac{屏幕的x差分辨率}{摄像机到准星的距离分辨率} $$
$$ \tan{x差角度}=\frac{屏幕的x差分辨率}{\frac{\frac{屏幕水平分辨率}{2}}{\tan{\frac{水平FOV}{2}}}} $$
$$ 屏幕的x差分辨率=\frac{\frac{屏幕水平分辨率}{2}}{\tan{\frac{水平FOV}{2}}}\times\tan{x差角度} $$
因为屏幕的左上角为(0,0),向右为x变大,向下为y变大,因此可知:
屏幕上指向敌人的x坐标 就是:屏幕的x差分辨率+水平分辨率/2
$$ \frac{垂直FOV}{水平FOV}=\frac{垂直分辨率}{水平分辨率} $$ $$ 屏幕的y差分辨率=\frac{\frac{屏幕垂直分辨率}{2}}{\tan{\frac{垂直FOV}{2}}}\times\tan{y差角度} $$
$$ 屏幕的y差分辨率=\frac{\frac{屏幕水平分辨率}{2}}{\tan{\frac{水平FOV}{2}}}\times\tan{y差角度} $$
屏幕上指向敌人的y坐标 就是:屏幕的y差分辨率+垂直分辨率/2
总结 $$ 屏幕坐标X=\frac{\frac{屏幕水平分辨率}{2}}{\tan{\frac{水平FOV}{2}}}\times\tan{x差角度}+\frac{水平分辨率}{2} $$
$$ 屏幕坐标Y=\frac{\frac{屏幕水平分辨率}{2}}{\tan{\frac{水平FOV}{2}}}\times\tan{y差角度}+\frac{垂直分辨率}{2} $$
算法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #define PI 3.1415926 class 坐标结构{ public : int X,Y; }; float 弧度转角度(float 弧度){ return 弧度*180 /PI; } float 角度转弧度(float 角度){ return 角度*PI/180 ; } bool 世界坐标转屏幕坐标_非矩阵(坐标结构& 屏幕坐标,float 水平角度差,float 垂直角度差,float 水平FOV){ 取窗口信息(水平分辨率,垂直分辨率); float 摄像机到准星的距离分辨率=水平分辨率/2 /(tan (角度转弧度(水平FOV/2 ))); float 高低最大可视角度=弧度转角度(atan2 (垂直分辨率/2 ,摄像机到准星的距离分辨率)); if (fabs (水平角度差)>水平FOV/2 ||fabs (垂直角度差)>高低最大可视角度) { return false ; } int 水平差=摄像机到准星的距离分辨率*tan (角度转弧度(水平角度差)); 屏幕坐标.X=水平差+水平分辨率/2 ; int 垂直差=摄像机到准星的距离分辨率*tan (角度转弧度(垂直角度差)); 屏幕坐标.Y=垂直差+垂直分辨率/2 ; return true ; }
无矩阵方式为:获取人物与目标的世界坐标高度差和世界水平距离来得到人物和目标的夹角,再通过人物准星减去目标夹角(或反过来)来确定准星偏转角度差 ,再通过准星偏转角度差和游戏宽高分辨率来转换成屏幕坐标进行绘图
这种方式得到的坐标绘制多多少少有误差,下面用到矩阵的方式无误差
相关代码截取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 struct Point2D { float X, Y; }; struct Vector4 { float x; float y; float z; float w; }; class Paint { public: HWND m_hWnd; RECT m_wndRect; int m_resolutionWidth; int m_resolutionHeight; RECT m_outsideWnd; int m_outsideWndWidth; int m_outsideWndHeight; float m_viewMatrix[16 ]; DWORD m_matrixAddr32; DWORD64 m_matrixAddr64; void getWndInfo () ; public: Paint(HWND hWnd,DWORD matrixAddr=NULL , COLORREF brushColor=RGB(255 ,0 ,0 ), COLORREF penColor=RGB(255 ,0 ,0 )); Paint(HWND hWnd, DWORD64 matrixAddr, COLORREF brushColor = RGB(255 , 0 , 0 ), COLORREF penColor = RGB(255 , 0 , 0 )); Paint(); ~Paint(); HDC hdc; HBRUSH hBrush; HFONT hfont; HPEN hPen; bool worldPointToScreenPointWithoutMatrix (Point2D& screenPoint,const Orientation& angleDiff) ; bool worldPointToScreenPoint (Point2D& screenPoint, const Point3D& targetWorldPoint) ; bool worldPointToScreenPoint64 (Point2D& screenPoint, const Point3D& targetWorldPoint) ; void paintLine (int x, int y) ; void paintLine (int x, int y, int xTo, int yTo) ; void paintFrame (int x, int y, int w, int h, int thick) ; void paintFrameByFootAndHead (Point2D footPoiny,Point2D headPoint,int thick) ; void paintRect (int x,int y,int w,int h) ; void paintText (int x,int y,COLORREF color,const char * text) ; void beginPaint () ; void changeBrush (COLORREF brushColor) ; void changePen (int penStyle,int penWidth,COLORREF penColor) ; bool 世界坐标转屏幕坐标_非矩阵(Point2D& 屏幕坐标, FLOAT 水平角度差, FLOAT 高低角度差); }; float angleToRadian (float angle) { return (FLOAT)(angle*PI / 180 ); } float radianToAngle (float radian) { return (FLOAT)(radian * 180 / PI); } void Paint::getWndInfo () { GetClientRect(m_hWnd,&m_wndRect); m_resolutionWidth = m_wndRect.right - m_wndRect.left; m_resolutionHeight = m_wndRect.bottom - m_wndRect.top; GetWindowRect(m_hWnd, &m_outsideWnd); m_outsideWndWidth = m_outsideWnd.right - m_outsideWnd.left; m_outsideWndHeight = m_outsideWnd.bottom - m_outsideWnd.top; } bool Paint::世界坐标转屏幕坐标_非矩阵(Point2D& 屏幕坐标, FLOAT 水平角度差, FLOAT 高低角度差){ getWndInfo(); FLOAT 高低可视角度 = (FLOAT)((double )atan2 (m_resolutionHeight, m_resolutionWidth) * 180 / 3.1415 ); if (fabs (水平角度差) > 45 || fabs (高低角度差) > 高低可视角度) { return false ; } int 水平差 = (int )(tan (水平角度差 * 3.1416 / 180 ) * ((m_resolutionWidth) / 2 )); 屏幕坐标.X = (float )(m_resolutionHeight / 2 + 水平差); int 高度差 = (int )(tan (高低角度差 * 3.1416 / 180 ) * ((m_resolutionWidth) / 2 )); 屏幕坐标.Y = (float )(m_resolutionHeight / 2 + 高度差); return true ; } void EntityAround::calOrientation (Point3D& targetLoc,Orientation& targetAngle,Orientation& angleDiff) { FLOAT fov_X = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x195fe58 ); FLOAT fov_Y = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x195fe5c ); FLOAT fov_Z = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x195fe60 ); FLOAT fov_horizon= *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x19E10C8 ); FLOAT fov_vertical = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x19E10C4 ); if (targetLoc.X >= fov_X&&targetLoc.Y >= fov_Y) { targetAngle.horizon= radianToAngle(atan2 (targetLoc.Y - fov_Y, targetLoc.X - fov_X)); } else if (targetLoc.X <= fov_X&&targetLoc.Y >= fov_Y) { targetAngle.horizon = 180 - radianToAngle(atan2 (targetLoc.Y - fov_Y, fov_X - targetLoc.X)); } else if (targetLoc.X <= fov_X&&targetLoc.Y <= fov_Y) { targetAngle.horizon = 180 + radianToAngle(atan2 (fov_Y - targetLoc.Y, fov_X - targetLoc.X)); } else if (targetLoc.X >= fov_X&&targetLoc.Y <= fov_Y) { targetAngle.horizon = 360 - radianToAngle(atan2 (fov_Y - targetLoc.Y, targetLoc.X - fov_X)); } FLOAT distance = sqrt ((targetLoc.X - fov_X)*(targetLoc.X - fov_X) + (targetLoc.Y - fov_Y)*(targetLoc.Y - fov_Y)); if (targetLoc.Z >= fov_Z) { targetAngle.vertical = -radianToAngle(atan2 (targetLoc.Z - fov_Z, distance)); } else { targetAngle.vertical = radianToAngle(atan2 (fov_Z - targetLoc.Z, distance)); } angleDiff.horizon = fov_horizon - targetAngle.horizon; if (angleDiff.horizon<=-180 ) { angleDiff.horizon += 360 ; } if (angleDiff.horizon>180 ) { angleDiff.horizon -= 360 ; } angleDiff.vertical = targetAngle.vertical - fov_vertical; }
需要矩阵 矩阵分行主序与列主序,主要是d3d和openGl的区别
行主序(D3D)
1 2 3 4 a1 a2 a3 a4 x a1*x+a2*y+a3*z+a4*w b1 b2 b3 b4 乘以 y = b1*x+b2*y+b3*z+b4*w c1 c2 c3 c4 z c1*x+c2*y+c3*z+c4*w d1 d2 d3 d4 w d1*x+d2*y+d3*z+d4*w
列主序(openGl)
1 2 3 4 a1 b1 c1 d1 a1*x+a2*y+a3*z+a4*w x,y,z,w 乘以 a2 b2 c2 d2 = b1*x+b2*y+b3*z+b4*w a3 b3 c3 d3 c1*x+c2*y+c3*z+c4*w a4 b4 c4 d4 d1*x+d2*y+d3*z+d4*w
缩放和位移矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Sx 0 0 Tx 0 Sy 0 Ty0 0 Sz Tz0 0 0 TwSx 0 0 0 0 Sy 0 0 0 0 Sz 0 Tx Ty Tz Tw Sx*x+w*Tx x,y,z,w乘以列主序= Sy*y+w*Ty Sz*z+w*Tz w*Tw
[第一结论] : 行主序最后一列,列主序最后一行,走路,跳,会发生改变,不代表别的动作不改变
旋转矩阵 绕z轴旋转矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 绕z轴旋转矩阵(A表示点逆时针旋转角度 或 坐标系顺时针旋转角度) cosA -sinA 0 0 sinA cosA 0 0 0 0 1 0 0 0 0 1 cosA sinA 0 0 -sinA cosA 0 0 0 0 1 0 0 0 0 1 xcosA-ysinA x,y,z,w乘以列主序= xsinA+yconsA z w
[第二结论] : 水平转动的情况,行主序第三列不变,列主序第三行不变
绕x轴旋转矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 绕x轴旋转矩阵(A表示点逆时针旋转角度 或 坐标系顺时针旋转角度) 1 0 0 0 0 conA -sinA 0 0 sinA cosA 0 0 0 0 1 1 0 0 0 0 conA sinA 0 0 -sinA cosA 0 0 0 0 1 x x,y,z,w乘以列主序= -zsinA+yconsA ysinA+zcosA w
[第三结论] : 高低朝向改变的时候,行主序第一行不变,列主序第一列不变
绕y轴旋转矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 cosA 0 sinA 0 0 1 0 0 -sinA 0 cosA 0 0 0 0 1 cosA 0 -sinA 0 0 1 0 0 sinA 0 cosA 0 0 0 0 1 xcosA+zsinA x,y,z,w乘以列主序= y -xsinA+zcosA w
矩阵变换总结 找矩阵总结 分类
总结
[第一结论] : 行主序最后一列,列主序最后一行,走路,跳,会发生改变(只影响这行或列),不代表别的动作不改变)
[第二结论] : 水平转动的情况,行主序第三列(或第二列)不变,列主序第三行(或第二行)不变
[第三结论] : 高低朝向改变的时候,行主序第一行不变,列主序第一列不变
下面的结论不绝对
**[第四结论]**矩阵第一个值往往是-1到1之间的(只有一部分)
**[第五结论]**行主序第一行第三个(或第二个)元素是固定的0,列主序第一列的第三个(或第二个)元素是0
**[第六结论]**开倍镜,第一个值会乘以相应的倍数(这个是绝对的)
**[第七结论]**列主序最后两列前三值相似,行主序最后两行前三值相似(只做参考)
**[第八结论]**随便走,不动准星,会有一行或一列4个在变化(如果只朝x或y前进,那么也只改变3个值,即行主序最后一列第一个与列主序最后一行第一个当沿着x或y方向走也不变),跳就只有三个值在一行或一列变化[第八结论UE4矩阵不符合,可参考下面的UE4专题]
经验:
找矩阵的时候一批次只用确定一次
找矩阵的时候,优先找多号码段连排的
基本步骤
鼠标移动,搜变动
人物移动,跳跃等搜不变动
如果剩余过多尝试不绝对结论减少数量(风险)
剩余不多数量的情况下,每个打开看看移动是不是四个连着的数据改变,跳跃是不是三个连着的数据改变
[找矩阵实例](# 找矩阵)
世界坐标转换为屏幕坐标
剪辑坐标 :理解为三维世界的切片
NDC坐标 (标准化设备坐标)
就是将剪辑坐标的范围缩小至-1到1,如图:
世界坐标–>剪辑坐标–>NDC坐标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 a0 a1 a2 a3 x,y,z,w 乘以 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 剪辑坐标x = a0*x+a4*y+a8*z+a12*w 剪辑坐标y = a1*x+a5*y+a9*z+a13*w 剪辑坐标z = a2*x+a6*y+a10*z+a14*w 剪辑坐标w = a3*x+a7*y+a11*z+a15*w NDC.x=剪辑坐标x/剪辑坐标w NDC.y=剪辑坐标y/剪辑坐标w NDC.z=剪辑坐标z/剪辑坐标w
NDC坐标–>屏幕坐标
实际上就是坐标系的转换,NDC坐标系转换成屏幕分辨率坐标系.
$$
\frac{目标点的NDC坐标X}{2}=\frac{屏幕坐标差x}{显示器分辨率宽}
\\
\\
\frac{目标点的NDC坐标y}{2}=\frac{屏幕坐标差y}{显示器分辨率高}
$$
1 2 3 目标点的屏幕坐标差x=目标点的NDC坐标X*显示器分辨率宽/2 目标点的屏幕坐标差y=目标点的NDC坐标y*显示器分辨率高/2
因此NDC坐标—>屏幕坐标算法 计算如下: $$ 目标点的屏幕坐标x=\frac{目标点的NDC坐标X\times分辨率宽}{2}+\frac{分辨率宽}{2} $$ $$ 目标点的屏幕坐标y=\frac{分辨率高}{2}-\frac{目标点的NDC坐标y\times分辨率高}{2} $$
坐标点A和坐标点B均适用,说明四象限全适用.
相关代码截取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 struct Point2D { float X, Y; }; struct Vector4 { float x; float y; float z; float w; }; class Paint { public: HWND m_hWnd; RECT m_wndRect; int m_resolutionWidth; int m_resolutionHeight; RECT m_outsideWnd; int m_outsideWndWidth; int m_outsideWndHeight; float m_viewMatrix[16 ]; DWORD m_matrixAddr32; DWORD64 m_matrixAddr64; void getWndInfo () ; public: Paint(HWND hWnd,DWORD matrixAddr=NULL , COLORREF brushColor=RGB(255 ,0 ,0 ), COLORREF penColor=RGB(255 ,0 ,0 )); Paint(HWND hWnd, DWORD64 matrixAddr, COLORREF brushColor = RGB(255 , 0 , 0 ), COLORREF penColor = RGB(255 , 0 , 0 )); Paint(); ~Paint(); HDC hdc; HBRUSH hBrush; HFONT hfont; HPEN hPen; bool worldPointToScreenPointWithoutMatrix (Point2D& screenPoint,const Orientation& angleDiff) ; bool worldPointToScreenPoint (Point2D& screenPoint, const Point3D& targetWorldPoint) ; bool worldPointToScreenPoint64 (Point2D& screenPoint, const Point3D& targetWorldPoint) ; void paintLine (int x, int y) ; void paintLine (int x, int y, int xTo, int yTo) ; void paintFrame (int x, int y, int w, int h, int thick) ; void paintFrameByFootAndHead (Point2D footPoiny,Point2D headPoint,int thick) ; void paintRect (int x,int y,int w,int h) ; void paintText (int x,int y,COLORREF color,const char * text) ; void beginPaint () ; void changeBrush (COLORREF brushColor) ; void changePen (int penStyle,int penWidth,COLORREF penColor) ; bool 世界坐标转屏幕坐标_非矩阵(Point2D& 屏幕坐标, FLOAT 水平角度差, FLOAT 高低角度差); }; void Paint::getWndInfo () { GetClientRect(m_hWnd,&m_wndRect); m_resolutionWidth = m_wndRect.right - m_wndRect.left; m_resolutionHeight = m_wndRect.bottom - m_wndRect.top; GetWindowRect(m_hWnd, &m_outsideWnd); m_outsideWndWidth = m_outsideWnd.right - m_outsideWnd.left; m_outsideWndHeight = m_outsideWnd.bottom - m_outsideWnd.top; } Vector4 RowVecTimesMatrix (const Vector4& rowVec, float matrix[16 ]) { Vector4 retVec; retVec.x = rowVec.x*matrix[0 ] + rowVec.y*matrix[4 ] + rowVec.z*matrix[8 ] + rowVec.w*matrix[12 ]; retVec.y = rowVec.x*matrix[1 ] + rowVec.y*matrix[5 ] + rowVec.z*matrix[9 ] + rowVec.w*matrix[13 ]; retVec.z = rowVec.x*matrix[2 ] + rowVec.y*matrix[6 ] + rowVec.z*matrix[10 ] + rowVec.w*matrix[14 ]; retVec.w = rowVec.x*matrix[3 ] + rowVec.y*matrix[7 ] + rowVec.z*matrix[11 ] + rowVec.w*matrix[15 ]; return retVec; } bool Paint::worldPointToScreenPoint (Point2D & screenPoint,const Point3D& targetWorldPoint) { getWndInfo(); memcpy (&m_viewMatrix, (PVOID)m_matrixAddr32, sizeof (m_viewMatrix)); Vector4 worldLocation = { targetWorldPoint.X,targetWorldPoint.Y,targetWorldPoint.Z,1 }; Vector4 cutLocation= RowVecTimesMatrix(worldLocation, m_viewMatrix); if (cutLocation.w<0.0 ) { return false ; } Point2D NDC; NDC.X = cutLocation.x / cutLocation.w; NDC.Y = cutLocation.y / cutLocation.w; screenPoint.X = (NDC.X*m_resolutionWidth + m_resolutionWidth) / 2 ; screenPoint.Y = (m_resolutionHeight - m_resolutionHeight*NDC.Y) / 2 ; return true ; } bool Paint::worldPointToScreenPoint64 (Point2D & screenPoint, const Point3D& targetWorldPoint) { getWndInfo(); memcpy (&m_viewMatrix, (DWORD64*)m_matrixAddr64, sizeof (m_viewMatrix)); Vector4 worldLocation = { targetWorldPoint.X,targetWorldPoint.Y,targetWorldPoint.Z,1 }; Vector4 cutLocation = RowVecTimesMatrix(worldLocation, m_viewMatrix); if (cutLocation.w < 0.0f ) { return false ; } Point2D NDC; NDC.X = cutLocation.x / cutLocation.w; NDC.Y = cutLocation.y / cutLocation.w; screenPoint.X = (NDC.X*m_resolutionWidth + m_resolutionWidth) / 2 ; screenPoint.Y = (m_resolutionHeight - m_resolutionHeight*NDC.Y) / 2 ; return true ; }
朝向(准星) 准星数据寻找
在fps游戏中,横轴是可以无限原地转圈的,而竖轴上下大概在180度左右,所以Y往往比X更合适用来当作这一数据的突破口。那么假设准星数据做了处理,应该如何找到瞄准call呢?搜索出与准星坐标相关的地址后,移动鼠标进行访问断,就有可能断到相关的函数。
朝向极大概率是浮点数。
需要找到x,y轴坐标对应的朝向。
矩阵补充 通常情况下Dx9中会采用4*4的矩阵
这里说的横矩阵是列主序,列矩阵是行主序
//荒野行动横矩阵第3排,第4列的数,光标朝天看的时候是1,朝地下是-1。(此结论通用性待验证)
补充一个3*4列矩阵
1 2 3 4 5 X X X x X X X y X X X z 0 0 0 比例3 *4 (列矩阵)
UE4矩阵
1 2 3 4 5 6 7 8 9 10 11 12 UE4最常见的两种矩阵 //第一种横矩阵格式(上+下-)(这就是上图中荒野行动那种) xxx xxx 0 xxxx xxx xxx 0 xxxx xxx xxx 0 xxxx X Y 比例 Z //第二种横矩阵格式(上-下+) xxx xxx xxxx 0 xxx xxx xxxx 0 xxx xxx xxxx 0 X Y Z 比例 //特征:除了XYZ,其他所有数值在(-2,+2)间
普通食物4*4横矩阵和竖矩阵的矩阵头在这个区间(-2,+2)
针对自动瞄准,应该如何检测:
1、模拟类自瞄
一、Hook windows所提供的所有按键操作、鼠标操作类接口,函数全部由游戏安全系统接管。
二、既然Hook了相关函数,那么就可以被突破,Hook被还原,还要在代码段上下crc,防止被还原Hook,在crc上多次套crc,多设防御关卡。
三、检测鼠标轨迹和移动速度,没有谁可以一直直线移动鼠标。
2、内存类自瞄
一、Hook windows所提供的所有“写内存”类接口,如WriteProcessMemory(),函数全部由游戏安全系统接管,检测到非自身或者未知程序调用该类型函数对游戏进行操作时,将其踢下线。
二、同模拟类自瞄第二条。
三、同模拟类自瞄第三条。
四、加密鼠标横轴(x坐标),且多个地址同步准星数据,当非法修改了准星位置,即准星数据不同步,即可知道非法修改了内存。
那么接下来,同笔者一起分析,透视自瞄究竟是如何实现的呢?
其实只要实现了自瞄,就相当于实现了透视,只是需要开发者通过一系列算法将其绘制在屏幕上而已。而实现自瞄,对于有相关工作经验的人来说,也算不上难,一套公式,几乎“通杀”所有FPS。自瞄算法可以简单的分为两种,一种通过玩家坐标和准星位置进行计算,一种为”矩阵自瞄“,计算模型骨骼等等,其实两种自瞄本质意义上区别不大,各有各的好处,只不过第二种更为精准,第一种则更为方便,需要的数据更少。
自己封装内存模块用到的api
打开进程===OpenProcess
打开当前进程===OpenProcess
写内存===WriteProcessMemory
关闭句柄===CloseHandle
读取内存整数===ReadProcessMemory
保护内存===VirtualQueryEx
读内存字节集===ReadProcessMemory
提示自身进程权限===RtlAdjustPrivilege
获取当前进程id===GetCurrentProcessId
取一个窗口的标题===GetWindowTextA
寻找窗口句柄===FindWindowExA
获取窗口客户区===GetClientRect
获取窗口类名===GetClassNameA
终止进程===TerminateProcess
获取快照信息===CreateToolhelp32Snapshot
获取遍历快照的第一个进程===Process32First
获取遍历快照的下一个进程===Process32Next
获取遍历快照的第一个模块===MODULE32First
获取遍历快照的下一个模块===MODULE32Next
1 2 3 4 UE4 Count (环境数量)→ -游戏类型 → 数组Actor ↓ ↓ Uworld (世界地址) 人物坐标XYZ 血量 阵容 飞天
1 2 3 4 5 X Y Z地址"UE4Game-Win64-Shipping.exe"+02CC5F50]+588]+158]+1D8 [[[[[7FF714B28BF0+88+rcx*3*8+8]+10]+50]+270]+158]+1D0==233F0434FE0
CE找数据技巧 找数据技巧:排除栈地址,排除ui上显示的数据信息,排除追踪到服务器模块的情况(如果本机有服务器的话)
大量30~80的字符表示的是ascii字符串,4或C开头的4字节大概率是浮点数
对于服务器和客户端模块都在本地的游戏,使用一些确定是服务器数据的数据来找来源,来源于的模块大概率就是服务器模块
追任何偏移,如果CE能直接搜索到对应基地址,则可直接使用基地址
可以通过call内的ret来判断这个call有几个参数,而不仅仅是通过找call前的push数量
dbg配合ce找下属偏移的高级技巧 角色对象附近地址的属性都可以直接尝试定位,甚至可以到CE搜所有相同结果,找所有结果中最靠近对象地址附近的结果,然后通过 所有结果中最靠近对象地址-对象首地址来得到猜测的偏移.这时候直接在最靠近对象的地址下访问断,看能不能断到偏移就是猜测偏移的地址 该技巧使用实例
针对单机游戏如下:
无限子弹通过对子弹数下写入断就可以断到,断到的位置往上翻找子弹数减少1,修改此处可以无限子弹.往上追上层call一定会追到射击call,射击call附近应该有射速相关的参数,可能是射击间隔作为参数,也可能是别的;(老游戏每个枪可能都有一个射击call,但是往上追会追到一个通用的射击call).每个call进行屏蔽,来确定哪个是负责后坐力的,简单粗暴把负责后坐力的nop掉,将call改成add esp,参数字节数,就实现了没有后坐力.找无敌可以到血量下写入断点,断到的位置的call很可能就是扣血call,nop掉就不扣血了.放雷的call中,nop掉[所有可能跳过将雷的数量减一放入某地址的操作],就实现了疯狂丢雷无间隔.
逆向注意点:下图这样的lea一个堆栈地址的情况,要这么处理
CS1.6的逆向 找内存数据 通过客户端或服务端来搜数据排查服务端的模块是哪一个
找血量数据如下:
只有蓝标数据真正可以修改目标血量,说明这个数据是服务端数据,到x32dbg中下写入断点来确定服务器模块,击中目标的瞬间断到下图位置
观察红线标记的模块,可知服务器模块应该就是mp.dll,可对其他数据进行下断确定不经过该模块.
回头发现,只有鼠标经过指向目标才变回来的是个基地址,第三行数据往上追的过程中经过了mp.dll模块,因此最终确定到第二行数据才是真是数据,而不是第三行.
在x32dbg中追第二行数据:
1 2 3 得出队友血量的地址=[0x1A17CB8 ]*0x68 +0x1A2565C 因此遍历实体血量:n*0x68 +0x1A2565C
比对数组项之间的差异,结构体大小为0x68(公式分析得出),-0x30的目的是因为我们找到的有可能并不是数组项的起始地址
0和5下标是边界外,游戏目前数据只有1~4,1,2,4是队友,3是敌人
打死队友发现其对应的生命不归零,因此需要找死亡标志
1 2 3 4 5 +0x4E BYTE +0x60 BYTE +0x68 DWORD
我们还要找坐标的数据结构:
通过反复让敌人行动找到了坐标,锁定才能触发屏闪的位置,不锁定什么反应都没有的情况,下硬件写入断点直接崩溃.
发现必须找到是不锁定的情况下修改坐标人物抖动以下,这个才是真正要找的目标坐标.发现他是一个基地址:h1.exe+1B5A998
并且在其附近发现了目标的名称
下硬件访问断点开始追该地址
断在的两个位置(分别是下图两次标黄)往上追都不好追,追丢了.但可以猜测出最末一层的结构体偏移为0x188
尝试不直接断坐标数据,而是断前后几个数据之一,断到的位置往上追一层偏移,放开断点后发现上一层偏移的地址中存的值变成了0,即这个地址是个临时的地址,也不能用
尝试在名字下访问断,断下的位置发现有一个基地址偏移,因此大概率是可以追到数据结构的.
上图可知:
确定结构体首地址:
上图可知坐标比名字大0x84,前面有知道名字的末层偏移是0x188
因此名字的末层偏移为0x104
1 2 3 4 5 6 7 8 9 10 +0x0 DWORD +0x104 ASCII +0x130 ASCII +0x180 FLOAT +0x188 FLOAT X坐标 +0x18C FLOAT Y坐标 +0x190 FLOAT Z坐标
由于上面结构并没有本人坐标,因此下面逆向本人坐标
找本人坐标的时候,其特点是锁定数值后,修改数值,人物产生位移,解除锁定后,动下人物,人物瞬移回来
另外一种情况是锁定后,修改,不断的人物屏闪,这种内存也能用,但是最好不要下断,太快了,容易崩溃
要注意移动到一个位置后要等他完全静止才搜变动,因为有些内存更改比较慢(上面非屏闪情况就是这种)
1 2 3 4 5 6 7 8 9 10 11 12 13 h1.exe+1B 5A528 FLOAT X坐标 h1.exe+1B 5A52C FLOAT Y坐标 h1.exe+1B 5A530 FLOAT Z坐标 h1.exe+19E3 F28 h1.exe+19f 0750 h1.exe+19F 4A08 h1.exe+195F E58 h1.exe+1B 5A508 FLOAT 垂直朝向 h1.exe+1B 5A50C FLOAT 水平朝向
在x32dbg中简单观察了一下,初步确认不是来自于mp.dll的服务器数据
利用上面找到的朝向数值来找真正的准星内存:(可以修改准星位置的内存数据)
1 2 3 4 h1.exe+19E10 C4 FLOAT 垂直朝向 h1.exe+19E10 C8 FLOAT 水平朝向
找矩阵 [找矩阵方法总结](# 找矩阵总结)
按照上述方法找到的矩阵地址为: 0x2c20100 = hl.exe+1820100
口袋西游逆向 口袋西游逆向参见详见51和53课
骨骼和模型不一定有绝对坐标,但是一般都有相对坐标
基本数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [[[D0DF1C]+1 C]+28 ]+3 C [[[D0DF1C]+1 C]+28 ]+40 [[[D0DF1C]+1 C]+28 ]+44 [[[D0DF1C]+1 C]+28 ]+560 [[[D0DF1C]+1 C]+28 ]+564 [[[D0DF1C]+1 C]+28 ]+568 [[[D0DF1C]+1 C]+28 ]+56 C [[[D0DF1C]+1 C]+28 ]+570 [[[D0DF1C]+1 C]+28 ]+574 [[[D0DF1C]+1 C]+28 ]+578 [[[D0DF1C]+1 C]+28 ]+57 C [[[D0DF1C]+1 C]+28 ]+580 [[[D0DF1C]+1 C]+28 ]+584 [[[D0DF1C]+1 C]+28 ]+588 [[[D0DF1C]+1 C]+28 ]+58 C [[[[D0DF1C]+1 C]+8 ]+20 ]+5 C [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+3 C [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+40 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+44 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+ED [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+138 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 C4 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 C8 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 CC [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 D0 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 D4 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 D8 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 DC [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2E0 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2E4 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2E8 [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2 EC [[[[[[[D0DF1C]+1 C]+8 ]+20 ]+58 ]+N*4 ]+4 ]+2F 0
找矩阵 [找矩阵方法总结](# 找矩阵总结)
矩阵头地址为: [[[ELEMENTCLIENT.EXE+0x90DF1C]+14]+8]+E8
[ELEMENTCLIENT.EXE+92E5D0]+E8 也可以
突袭的逆向 基本数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [587 C0C]+28 [587 C0C]+2 C [587 C0C]+30 [587 C0C]+34 [587 C0C]+38 [587 C0C]+EC 587 C18 [[587 C10]+n*4 ]+4 [[587 C10]+n*4 ]+8 [[587 C10]+n*4 ]+C [[587 C10]+n*4 ]+28 [[587 C10]+n*4 ]+2 C [[587 C10]+n*4 ]+30 [[587 C10]+n*4 ]+34 [[587 C10]+n*4 ]+38 [[587 C10]+n*4 ]+50 [[587 C10]+n*4 ]+EC
找矩阵 [找矩阵方法总结](# 找矩阵总结)
矩阵头地址: ac_client.exe+17AFE0
UE4逆向 UE4引擎特点
增加新对象时,即更换物品时(物品从不可见无中生有)内存几乎一直在增加
世界中所有万物都在数组结构中,存在数组数量(物品从不可见无中生有的情况,数量会增加,游戏只要不退出而是当前地图重新开始,数量也会增加;只有游戏退出或更换地图重新开始,数量才会重置,同时存数组数量的地址也会发生变化)
子弹未命中的情况下不会增加实体,子弹只有命中的情况下才会增加实体
数组数量:几乎没有减少的可能,基本上一直在增加
UE4的坐标特点:
在CE结构对比工具中,因为z后的0和x下的1被合并误识别为double类型,因此是如图:
在内存中:
1 2 3 4 5 6 7 8 0 0 1 1 1 1 0 0 1 0 (全为浮点数)00 00 00 00 00 00 00 00 00 00 80 3F 00 00 80 3F 00 00 80 3F 00 00 80 3F 00 00 00 00 00 00 00 00 00 00 80 3F 00 00 00 00 00 00 00 00 00 00 80 3F 00 00 80 3F 00 00 00 00
UE4矩阵:
上图中红框第三列0,0,0,x永远不变.(不绝对)
四处走动时,最后一行除了1其他3个都变(沿着x,y走两个值改变);跳跃时,最后一行除了1和第一个,其他两个变(不绝对)
第一个值-1.19到+1.19(不绝对)
矩阵下方那排都是0,0,0,1?
参考pubg矩阵:
Battle Royale Trainer逆向 吃鸡模拟器
基本数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [["BattleRoyaleTrainer-Win64-Shipping.exe" +2 AF0FB8]+138 ]+0xB8 [[[[["BattleRoyaleTrainer-Win64-Shipping.exe" +2 AD9F30]+[(["BattleRoyaleTrainer-Win64-Shipping.exe" +2B 00648]+40 )]的低4 字节*3 *8 ]+20 ]+c0]+138 ]+0xB8 [["BattleRoyaleTrainer-Win64-Shipping.exe" +2 AF0FB8]+138 ]+0xB0 [[["BattleRoyaleTrainer-Win64-Shipping.exe" +2 AF0FB8]+138 ]+0xB0 ]+N*8 [对象首地址+158 ]+190 X [对象首地址+158 ]+194 Y [对象首地址+158 ]+198 Z [[世界对象数组地址+n*8 ]+158 ]+190 X [[世界对象数组地址+n*8 ]+158 ]+194 Y [[世界对象数组地址+n*8 ]+158 ]+198 Z [世界对象地址+n*8 ]得到的是对象结构首地址,再+158 ]进入一个存放了坐标信息的结构首地址.再+190 ]取到坐标x或y值
此位置可以断到多个人物对象,通过查看对应坐标可确定是人物对象地址.通过上图黄标处多次断下的rcx可以得到所有人物对象地址.
找人物特征
用ce比对工具如上图(前一组两列是两个人物,后一组是其他对象),黄标处那两个很可能是人物特征
1 2 3 4 5 6 [对象首地址+0 ]== BattleRoyaleTrainer-Win64-Shipping.exe + 0x1D45740 [对象首地址+0xB9 ]==0x200 (即十进制512 ) 可判断是人物
[ 重点 ] 对象首地址+0的位置,代表的指针往往是指向虚表的(在非虚继承的情况下,虚函数表指针是存在于类的顶部的).其一样代表表示对象具有同样的成员函数(即对象具有同样的行为模板),因此[对象首地址+0]是最好用于判断是否同类对象的偏移.
排除做废对象
类似+0和+18和+35这样的位置都可以判断
我们发现做废对象,虚表指针也会改变,因此只需要使用上面人物标志的判断方式就可以避开做废对象.
人物存活判断 CE搜索找到自身血量的地址,然后通过上面找到的自己对象地址,通过自己血量地址-自己对象首地址得到血量偏移**(角色对象附近地址的属性都可以直接这样尝试定位,甚至可以到CE搜所有相同结果,找所有结果中最靠近对象地址附近的结果,然后通过 所有结果中最靠近对象地址-对象首地址来得到猜测的偏移.这时候直接在最靠近对象的地址下访问断,看能不能断到偏移就是猜测偏移的地址 )**
血量地址为: 人物对象首地址+0x7CC
人物死亡后,血量会<=0
武器相关 人物对象下存在一些数组结构,即地址+数量的组合.找到一个数组刚好和人物身上的武器数相同的数组结构,当把手榴弹掏出来的时候,数组数量还会加1.
进到数组首地址中,数组形式存储的这几个对象一般是对应1,2,3号键对应的武器.(顺序对应)
到武器对象下 ,可找到诸如射击间隔(射速),武器朝向,后坐力等浮点数数据,还有子弹数等.
射速和后坐力 等数据往往是武器创建后就固定的浮点数
可通过找武器对象下的浮点数(避开地址类型数据,防止崩溃,同时避开会变化的浮点数,因为那很可能是武器朝向)进行清0后游戏中尝试射击后还原,来找到后坐力,射速数据的位置.
1 2 3 4 5 6 武器对象+39 C 浮点数 射速(射击的间隔) 炸弹没有该参数,炸弹应该和枪结构不一致 武器对象+680 浮点数 初始上下抖动幅度 武器对象+684 浮点数 后续上下抖动幅度 武器对象+6 A4 浮点数 左右抖动幅度
骨骼数据 骨骼数据一般存有相对于人物参考点坐标的偏移坐标(人物参考点坐标可能有别于人物位置坐标)
通过身体上坐标变化比较多的身体部位来找骨骼数据
骨骼数据同样在人物对象下
骨骼数据往往是即使人站着的位置不动,骨骼数据也会轻微晃动(人物在微微晃动)
骨骼坐标数据如下:
黄标的是骨骼相对人物参考点坐标的偏移坐标;黄标上一行应该是骨骼的朝向信息,上图也可以看出这个骨骼的结构是个数组,每个re数组项占0x30字节.因此外围还存在数组地址和数组数量
[找的过程] 找到相对骨骼坐标地址后访问断往上追,追到下面地址处:
00007FF7A1EB25D9地址截图:
追到此处继续往上追发现非常难追了,尝试用ce分析. 此处用到的找偏移的理论方法
rbx有两个值,均到CE中搜索,发现其中一个有存放他的地址离对象首地址非常近.
上图猜测偏移结果为378
证实:
1 2 3 4 5 6 [[对象头地址+378 ]+698 +[[对象头地址+378 ]+6 DC]*10 ]+n*30 +10 [[对象头地址+378 ]+698 +[[对象头地址+378 ]+6 DC]*10 ]+n*30 +14 [[对象头地址+378 ]+698 +[[对象头地址+378 ]+6 DC]*10 ]+n*30 +18 [对象头地址+378 ]+6 A0
上面找到的只是相对参考点的偏移坐标,因此需要人物参考点坐标 .可以在到对象[头地址+378]下去找参考点坐标.按照逻辑猜测应该在这里
1 2 3 [对象头地址+378 ]+190 [对象头地址+378 ]+194 [对象头地址+378 ]+198
骨骼基准坐标大概率比人物位置坐标要低,一般是人物脚下的位置.还是实际到游戏中绘制一下看看是不是骨骼基准坐标最可靠.
找朝向 水平朝向值有-PI到+PI,0到360,-1到+1,非常多种.但很少出现特别大的值.
通过CE简单的搜索后,再通过手动修改来确定找到的是不是正确朝向
断到两个点,通过目标对象首地址配合CE找到目标数据 此处用到的找偏移的理论方法
1 2 3 4 5 [角色头地址+350 ]+36 C float [角色头地址+350 ]+368 float
找矩阵
矩阵地址: [["BattleRoyaleTrainer-Win64-Shipping.exe"+2ADA268]+24D08*8+8]+0x280
其中24D08的来源涉及非常复杂的加密流程,但是他是不变的.
上面项目代码盘点
entityAround.h 回到目录 跳转到源文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #pragma once #include <windows.h> struct Point3D { float X; float Y; float Z; }; struct Orientation { float horizon; float vertical; }; struct Entity { Point3D headPoint; Point3D footPoint; int hp; BYTE isDeath; bool isFriend; Orientation headAngle; Orientation footAngle; Orientation headAngleDifference; Orientation footAngleDifference; bool isNotNull=true ; }; #define PI 3.1415926 class EntityAround {public: Entity entity[0x100 ]; unsigned int entityNum; Entity closeToFrontSizeEntity ; void flashEntityAround_cs () ; void flashEntityAround_koudaixiyou () ; void flashEntityAround_tuxi () ; void flashEntityAround_chijimoniqi () ; void calOrientation (Point3D& targetLoc,Orientation& targetAngle,Orientation& angleDiff) ; }; float radianToAngle (float radian) ;float angleToRadian (float angle) ;void myOutPutDebug (const char * pszFormat, ...) ;
paint.h 回到目录 跳转到源文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #pragma once #include "entityAround.h" struct Point2D { float X, Y; }; struct Vector4 { float x; float y; float z; float w; }; class Paint { public: HWND m_hWnd; RECT m_wndRect; int m_resolutionWidth; int m_resolutionHeight; RECT m_outsideWnd; int m_outsideWndWidth; int m_outsideWndHeight; float m_viewMatrix[16 ]; DWORD m_matrixAddr32; DWORD64 m_matrixAddr64; void getWndInfo () ; public: Paint(HWND hWnd,DWORD matrixAddr=NULL , COLORREF brushColor=RGB(255 ,0 ,0 ), COLORREF penColor=RGB(255 ,0 ,0 )); Paint(HWND hWnd, DWORD64 matrixAddr, COLORREF brushColor = RGB(255 , 0 , 0 ), COLORREF penColor = RGB(255 , 0 , 0 )); Paint(); ~Paint(); HDC hdc; HBRUSH hBrush; HFONT hfont; HPEN hPen; bool worldPointToScreenPointWithoutMatrix (Point2D& screenPoint,const Orientation& angleDiff) ; bool worldPointToScreenPoint (Point2D& screenPoint, const Point3D& targetWorldPoint) ; bool worldPointToScreenPoint64 (Point2D& screenPoint, const Point3D& targetWorldPoint) ; void paintLine (int x, int y) ; void paintLine (int x, int y, int xTo, int yTo) ; void paintFrame (int x, int y, int w, int h, int thick) ; void paintFrameByFootAndHead (Point2D footPoiny,Point2D headPoint,int thick) ; void paintRect (int x,int y,int w,int h) ; void paintText (int x,int y,COLORREF color,const char * text) ; void beginPaint () ; void changeBrush (COLORREF brushColor) ; void changePen (int penStyle,int penWidth,COLORREF penColor) ; bool 世界坐标转屏幕坐标_非矩阵(Point2D& 屏幕坐标, FLOAT 水平角度差, FLOAT 高低角度差); }; Vector4 RowVecTimesMatrix (const Vector4& rowVec, float matrix[16 ]) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) ;LRESULT CALLBACK windowProc (HWND hMyWnd, UINT msg, WPARAM wParam, LPARAM lParam) ; void drawTransparentWnd () ;
stdafx.h 回到目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #pragma once #include "targetver.h" #define WIN32_LEAN_AND_MEAN #include <windows.h> #include "string" using namespace std ;
dllmain.cpp 回到目录 跳转到头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 #include "stdafx.h" #include "paint.h" #include "entityAround.h" void mainThread () { DWORD64 matrixAddr = *(DWORD64*)(*(DWORD64*)((DWORD64)GetModuleHandleA("BattleRoyaleTrainer-Win64-Shipping.exe" ) + 0x2ADA268 ) + 0x24D08 * 8 + 8 ) + 0x280 ; myOutPutDebug("matrix: %llx" , matrixAddr); Paint paint (FindWindow(L"UnrealWindow" , 0 ), matrixAddr) ; EntityAround entityArround; while (true ) { entityArround.flashEntityAround_chijimoniqi(); for (int i = 0 ; i < (int )entityArround.entityNum; i++) { Point2D screenFootPoint, screenHeadPoint; float footTmp = entityArround.entity[i].footPoint.Z; entityArround.entity[i].headPoint.X = entityArround.entity[i].footPoint.X; entityArround.entity[i].headPoint.Y = entityArround.entity[i].footPoint.Y; entityArround.entity[i].headPoint.Z = entityArround.entity[i].footPoint.Z+60 ; entityArround.entity[i].footPoint.Z = footTmp -100 ; if (paint.worldPointToScreenPoint64(screenFootPoint, entityArround.entity[i].footPoint)) { if (paint.worldPointToScreenPoint64(screenHeadPoint, entityArround.entity[i].headPoint)) { if (GetKeyState(VK_F3) & 1 ) { paint.paintFrameByFootAndHead(screenFootPoint, screenHeadPoint, 1 ); } } } } Sleep(1 ); if (GetKeyState(VK_F2) & 1 ) { break ; } } } HWND hMyWnd; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { myOutPutDebug("begin" ); ::WNDCLASSEXA winClass; winClass.lpszClassName = "zijiandialog" ; winClass.cbSize = sizeof (::WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; winClass.lpfnWndProc = windowProc; winClass.hInstance = hInstance; winClass.hIcon = 0 ; winClass.hIconSm = 0 ; winClass.hCursor = LoadCursor(NULL , IDC_ARROW); winClass.hbrBackground = NULL ; winClass.lpszMenuName = NULL ; winClass.cbClsExtra = 0 ; winClass.cbWndExtra = 0 ; RegisterClassExA(&winClass); hMyWnd = CreateWindowExA( 128 | 32 | 8 | WS_EX_LAYERED, "zijiandialog" , "自建窗口" , WS_EX_LAYERED | 0 | 0x10000 | 0x20000 | 0x2000000 | WS_POPUP, 100 , 100 , GetSystemMetrics(SM_CXSCREEN) * 640 / 1920 , GetSystemMetrics(SM_CYSCREEN) * 480 / 1080 , 0 , 0 , hInstance, 0 ); myOutPutDebug("hMyWnd: %d error: %d" , hMyWnd, GetLastError()); SetWindowPos(hMyWnd, (HWND)-1 , 100 , 100 , 0 , 0 , 19 ); ShowWindow(hMyWnd, SW_NORMAL); UpdateWindow(hMyWnd); SetWindowLongA(hMyWnd, -20 , 589992 ); SetLayeredWindowAttributes(hMyWnd, 0 , 1 , 2 ); SetLayeredWindowAttributes(hMyWnd, 0 , 0 , 1 ); MSG msg = { 0 }; while (true ) { ::SetWindowPos(hMyWnd, HWND_TOPMOST, 0 , 0 , 0 , 0 , SWP_NOSIZE | SWP_NOMOVE); DWORD dwStyle = ::GetWindowLong(hMyWnd, GWL_EXSTYLE); if (!((dwStyle & WS_EX_TOPMOST) == WS_EX_TOPMOST)) { SetWindowPos(hMyWnd, HWND_NOTOPMOST, 0 , 0 , 0 , 0 , SWP_NOSIZE | SWP_NOMOVE); } if (msg.message == WM_ERASEBKGND) { break ; } if (msg.message == WM_DESTROY || msg.message == WM_CLOSE || msg.message == WM_QUIT) { break ; } if (PeekMessage(&msg, hMyWnd, 0 , 0 , PM_REMOVE)) { DispatchMessage((&msg)); TranslateMessage(&msg); } Sleep(10 ); if (GetKeyState(VK_F3) & 1 ) { myOutPutDebug("quit" ); break ; } } return 0 ; } LRESULT CALLBACK windowProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: drawTransparentWnd(); return 0 ; case WM_SIZE: break ; case WM_CLOSE: case WM_DESTROY: PostQuitMessage(0 ); break ; default : break ; } return DefWindowProc(hWnd, msg, wParam, lParam); } EntityAround entityAround; HBRUSH hBrush; HDC hdc; Point2D screenFootPoint, screenHeadPoint; void drawTransparentWnd () { RECT myWnd; GetClientRect(hMyWnd, &myWnd); hdc = GetDC(hMyWnd); hBrush = CreateSolidBrush(RGB(0 , 0 , 0 )); FillRect(hdc, &myWnd, hBrush); DeleteObject(hBrush); Paint paintMyWnd (FindWindow(L"Valve001" , 0 ), (DWORD)GetModuleHandleA("hl.exe" ) + 0x1820100 ) ; paintMyWnd.getWndInfo(); MoveWindow(hMyWnd, paintMyWnd.m_outsideWnd.left + (paintMyWnd.m_outsideWndWidth - paintMyWnd.m_resolutionWidth) / 2 , paintMyWnd.m_outsideWnd.top + (paintMyWnd.m_outsideWndHeight - paintMyWnd.m_resolutionHeight - (paintMyWnd.m_outsideWndWidth - paintMyWnd.m_resolutionWidth) / 2 ), paintMyWnd.m_outsideWnd.right- paintMyWnd.m_outsideWnd.left, paintMyWnd.m_outsideWnd.bottom- paintMyWnd.m_outsideWnd.top, 1 ); entityAround.flashEntityAround_cs(); ReleaseDC(paintMyWnd.m_hWnd, paintMyWnd.hdc); paintMyWnd.m_hWnd = hMyWnd; paintMyWnd.hdc = hdc; paintMyWnd.paintText(paintMyWnd.m_resolutionWidth / 2 , paintMyWnd.m_resolutionHeight / 2 , RGB(255 , 0 , 0 ), "准星" ); for (int i = 0 ; i < (int )entityAround.entityNum; i++) { if (entityAround.entity[i].isDeath) { continue ; } if (entityAround.entity[i].isFriend) { continue ; } Point3D footPoint = { entityAround.entity[i].footPoint.X ,entityAround.entity[i].footPoint.Y ,entityAround.entity[i].footPoint.Z }; if (paintMyWnd.worldPointToScreenPoint(screenFootPoint, footPoint)) { Point3D headPoint = { entityAround.entity[i].headPoint.X ,entityAround.entity[i].headPoint.Y ,entityAround.entity[i].headPoint.Z }; if (paintMyWnd.worldPointToScreenPoint(screenHeadPoint, headPoint)) { float head = (float )screenHeadPoint.Y - (float )screenFootPoint.Y; float width = head / 2 ; float center = width / -2 ; float extra = head / -6 ; paintMyWnd.paintFrame((int )(screenFootPoint.X + center), (int )screenFootPoint.Y, (int )width, (int )(head - extra), 1 ); char healthChar[255 ]; sprintf_s(healthChar, sizeof (healthChar), "%d" , entityAround.entity[i].hp); paintMyWnd.paintText((int )screenFootPoint.X, (int )screenFootPoint.Y, RGB(255 , 0 , 0 ), healthChar); if (GetKeyState(VK_F4) & 1 ) { paintMyWnd.paintLine((int )screenFootPoint.X, (int )screenFootPoint.Y); } } } } } BOOL APIENTRY DllMain ( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hModule); CreateThread(0 , 0 , (LPTHREAD_START_ROUTINE)WinMain, 0 , 0 , 0 ); break ; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break ; } return TRUE; }
entityAround.cpp 回到目录 跳转到头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 #include "entityAround.h" #include <windows.h> #include <iostream> using namespace std ; DWORD g_baseAroundLoc = (DWORD)GetModuleHandleA("hl.exe" )+0x1B5A5C4 ; DWORD g_baseAroundInfo = (DWORD)GetModuleHandleA("hl.exe" ) + 0x62565C ; void EntityAround::flashEntityAround_cs () { BYTE myCam = *(BYTE*)(g_baseAroundInfo + 0x68 * 0 + 0x4E ); entityNum = 0 ; closeToFrontSizeEntity.isNotNull = 0 ; float theMiniAngleDiffSum = 999 ; for (int i = 1 ; i < 32 ; i++) { if (*(DWORD*)g_baseAroundLoc+0x24C *i==0 ) { break ; } if (*(DWORD*)g_baseAroundLoc + 0x24C * i+0x190 == 0 ) { continue ; } entity[entityNum].footPoint.X = *(FLOAT*)(g_baseAroundLoc + 0x24C * i + 0x188 ); entity[entityNum].footPoint.Y = *(FLOAT*)(g_baseAroundLoc + 0x24C * i + 0x18C ); entity[entityNum].footPoint.Z = *(FLOAT*)(g_baseAroundLoc + 0x24C * i + 0x190 )-52 ; entity[entityNum].headPoint.X = *(FLOAT*)(g_baseAroundLoc + 0x24C * i + 0x188 ); entity[entityNum].headPoint.Y = *(FLOAT*)(g_baseAroundLoc + 0x24C * i + 0x18C ); entity[entityNum].headPoint.Z = *(FLOAT*)(g_baseAroundLoc + 0x24C * i + 0x190 )+10 ; if (myCam== *(BYTE*)(g_baseAroundInfo + 0x68 * i + 0x4E )) { entity[entityNum].isFriend = true ; } else { entity[entityNum].isFriend = false ; } entity[entityNum].isDeath = *(BYTE*)(g_baseAroundInfo + 0x68 * i + 0x60 ); entity[entityNum].hp= *(DWORD*)(g_baseAroundInfo + 0x68 * i + 0x68 ); calOrientation(entity[entityNum].footPoint, entity[entityNum].footAngle, entity[entityNum].footAngleDifference); calOrientation(entity[entityNum].headPoint, entity[entityNum].headAngle, entity[entityNum].headAngleDifference); if (fabs (entity[entityNum].headAngleDifference.horizon)<45 &&fabs (entity[entityNum].headAngleDifference.vertical)<35 &&!entity[entityNum].isFriend&&!entity[entityNum].isDeath) { float tmp = fabs (entity[entityNum].headAngleDifference.horizon) + fabs (entity[entityNum].headAngleDifference.vertical); if (tmp < theMiniAngleDiffSum) { theMiniAngleDiffSum = tmp; closeToFrontSizeEntity = entity[entityNum]; } } entityNum++; } } void EntityAround::flashEntityAround_koudaixiyou () { DWORD temp = *(DWORD*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x8 )) + 0x20 ); entityNum = *(DWORD*)(temp + 0x5C ); DWORD entityBase = *(DWORD*)(temp + 0x58 ); for (int i = 0 ; i < (int )entityNum; i++) { entity[i].footPoint.X = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2DC ); entity[i].footPoint.Y = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2E0 ); entity[i].footPoint.Z = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2E4 ); entity[i].headPoint.X = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2E8 ); entity[i].headPoint.Y = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2EC ); entity[i].headPoint.Z = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2F0 ); entity[i].isFriend = 0 ; entity[i].hp= *(DWORD*)(*(DWORD*)(entityBase + 0x4 * i) + 0x138 ); entity[i].isDeath = *(BYTE*)(*(DWORD*)(entityBase + 0x4 * i) + 0xED ); } entityNum++; entity[entityNum - 1 ].footPoint.X = *(FLOAT*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x578 ); entity[entityNum - 1 ].footPoint.Y = *(FLOAT*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x57C ); entity[entityNum - 1 ].footPoint.Z = *(FLOAT*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x580 ); entity[entityNum - 1 ].headPoint.X = *(FLOAT*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x584 ); entity[entityNum - 1 ].headPoint.Y = *(FLOAT*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x588 ); entity[entityNum - 1 ].headPoint.Z = *(FLOAT*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x58C ); entity[entityNum - 1 ].isFriend = 0 ; entity[entityNum - 1 ].hp = *(DWORD*)((*(DWORD*)((*(DWORD*)((*(DWORD*)0xD0DF1C ) + 0x1C )) + 0x28 )) + 0x288 ); entity[entityNum - 1 ].isDeath = 0 ; } void EntityAround::flashEntityAround_tuxi () { entityNum = *(DWORD*)0x587C18 ; DWORD entityBase = *(DWORD*)0x587C10 ; for (int i = 1 ; i < (int )entityNum; i++) { entity[i].footPoint.X = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x28 ); entity[i].footPoint.Y = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x2C ); entity[i].footPoint.Z = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x30 ); entity[i].headPoint.X = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x4 ); entity[i].headPoint.Y = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0x8 ); entity[i].headPoint.Z = *(FLOAT*)(*(DWORD*)(entityBase + 0x4 * i) + 0xC ); entity[i].isFriend = 0 ; entity[i].hp = *(DWORD*)(*(DWORD*)(entityBase + 0x4 * i) + 0xEC ); if (entity[i].hp<=0 ) { entity[i].isDeath = 1 ; } else { entity[i].isDeath = 0 ; } } } DWORD64 worldAddress = (DWORD64)GetModuleHandleA("BattleRoyaleTrainer-Win64-Shipping.exe" ) + 0x2AF0FB8 ; DWORD64 humanVirtualTableFlag = (DWORD64)GetModuleHandleA("BattleRoyaleTrainer-Win64-Shipping.exe" ) + 0x1D45740 ; void EntityAround::flashEntityAround_chijimoniqi () { __try { DWORD worldCount = *(DWORD*)(*(DWORD64*)(*(DWORD64*)worldAddress + 0x30 ) + 0xB8 ); DWORD64 worldArrayAddr = *(DWORD64*)(*(DWORD64*)(*(DWORD64*)worldAddress + 0x30 ) + 0xB0 ); int realNo = 0 ; for (int i = 0 ; i < (int )worldCount; i++) { DWORD64 object = *(DWORD64*)(worldArrayAddr + i * 8 ); if (IsBadReadPtr((DWORD64*)object,8 )) { continue ; } if (*(DWORD64*)object!= humanVirtualTableFlag ) { continue ; } entity[realNo].hp = (DWORD)*(FLOAT*)(object + 0x7CC ); if (entity[realNo].hp<=0 ) { continue ; } entity[realNo].footPoint.X = *(FLOAT*)(*(DWORD64*)(object + 0x158 ) + 0x190 ); entity[realNo].footPoint.Y = *(FLOAT*)(*(DWORD64*)(object + 0x158 ) + 0x194 ); entity[realNo].footPoint.Z = *(FLOAT*)(*(DWORD64*)(object + 0x158 ) + 0x198 ); entity[realNo].isFriend = 0 ; entity[realNo].isDeath = 0 ; realNo++; } entityNum = realNo; myOutPutDebug("数量:%d" , realNo); } __except (1 ) { myOutPutDebug("something wrong1" ); } } void EntityAround::calOrientation (Point3D& targetLoc,Orientation& targetAngle,Orientation& angleDiff) { FLOAT fov_X = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x195fe58 ); FLOAT fov_Y = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x195fe5c ); FLOAT fov_Z = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x195fe60 ); FLOAT fov_horizon= *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x19E10C8 ); FLOAT fov_vertical = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe" ) + 0x19E10C4 ); if (targetLoc.X >= fov_X&&targetLoc.Y >= fov_Y) { targetAngle.horizon= radianToAngle(atan2 (targetLoc.Y - fov_Y, targetLoc.X - fov_X)); } else if (targetLoc.X <= fov_X&&targetLoc.Y >= fov_Y) { targetAngle.horizon = 180 - radianToAngle(atan2 (targetLoc.Y - fov_Y, fov_X - targetLoc.X)); } else if (targetLoc.X <= fov_X&&targetLoc.Y <= fov_Y) { targetAngle.horizon = 180 + radianToAngle(atan2 (fov_Y - targetLoc.Y, fov_X - targetLoc.X)); } else if (targetLoc.X >= fov_X&&targetLoc.Y <= fov_Y) { targetAngle.horizon = 360 - radianToAngle(atan2 (fov_Y - targetLoc.Y, targetLoc.X - fov_X)); } FLOAT distance = sqrt ((targetLoc.X - fov_X)*(targetLoc.X - fov_X) + (targetLoc.Y - fov_Y)*(targetLoc.Y - fov_Y)); if (targetLoc.Z >= fov_Z) { targetAngle.vertical = -radianToAngle(atan2 (targetLoc.Z - fov_Z, distance)); } else { targetAngle.vertical = radianToAngle(atan2 (fov_Z - targetLoc.Z, distance)); } angleDiff.horizon = fov_horizon - targetAngle.horizon; if (angleDiff.horizon<=-180 ) { angleDiff.horizon += 360 ; } if (angleDiff.horizon>180 ) { angleDiff.horizon -= 360 ; } angleDiff.vertical = targetAngle.vertical - fov_vertical; } float angleToRadian (float angle) { return (FLOAT)(angle*PI / 180 ); } void myOutPutDebug (const char * pszFormat, ...) { char szbufFormat[0x1000 ]; char szbufFormat_Game[0x1100 ] = "" ; va_list argList; va_start(argList, pszFormat); vsprintf_s(szbufFormat, pszFormat, argList); strcat_s(szbufFormat_Game, "FPS " ); strcat_s(szbufFormat_Game, szbufFormat); OutputDebugStringA(szbufFormat_Game); va_end(argList); } float radianToAngle (float radian) { return (FLOAT)(radian * 180 / PI); }
paint.cpp 回到目录 跳转到头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 #include "paint.h" #include <math.h> #include <stdio.h> #include <windows.h> void Paint::getWndInfo () { GetClientRect(m_hWnd,&m_wndRect); m_resolutionWidth = m_wndRect.right - m_wndRect.left; m_resolutionHeight = m_wndRect.bottom - m_wndRect.top; GetWindowRect(m_hWnd, &m_outsideWnd); m_outsideWndWidth = m_outsideWnd.right - m_outsideWnd.left; m_outsideWndHeight = m_outsideWnd.bottom - m_outsideWnd.top; } Paint::Paint(HWND hWnd, DWORD matrixAddr,COLORREF brushColor,COLORREF penColor) { m_hWnd = hWnd; m_matrixAddr32 = matrixAddr; if (matrixAddr!=NULL ) { memcpy (&m_viewMatrix, (VOID*)m_matrixAddr32, sizeof (m_viewMatrix)); } getWndInfo(); hdc = GetDC(hWnd); hBrush = CreateSolidBrush(brushColor); hPen = CreatePen(PS_SOLID, 1 , penColor); DeleteObject(SelectObject(hdc,hBrush)); DeleteObject(SelectObject(hdc, hPen)); } Paint::Paint(HWND hWnd, DWORD64 matrixAddr64, COLORREF brushColor, COLORREF penColor) { m_hWnd = hWnd; m_matrixAddr64 = matrixAddr64; if (matrixAddr64 != NULL ) { memcpy (&m_viewMatrix, (VOID*)m_matrixAddr64, sizeof (m_viewMatrix)); } getWndInfo(); hdc = GetDC(hWnd); hBrush = CreateSolidBrush(brushColor); hPen = CreatePen(PS_SOLID, 1 , penColor); DeleteObject(SelectObject(hdc, hBrush)) ; DeleteObject(SelectObject(hdc, hPen)); } Paint::Paint() { } Paint::~Paint() { if (hdc) { ReleaseDC(m_hWnd,hdc); } if (hBrush) { DeleteObject(hBrush); } if (hPen) { DeleteObject(hPen); } } bool Paint::worldPointToScreenPointWithoutMatrix (Point2D & screenPoint, const Orientation& angleDiff) { getWndInfo(); float maxVerticalViewAngle = radianToAngle(atan2 (m_resolutionHeight, m_resolutionWidth)); if (fabs (angleDiff.horizon) > 45 || fabs (angleDiff.vertical) > maxVerticalViewAngle) { return false ; } int horizonDiff = (int )(tan (angleToRadian(angleDiff.horizon))*((m_resolutionWidth) / 2 )); screenPoint.X = (float )(m_resolutionWidth / 2 + horizonDiff); int verticalDiff = (int )(tan (angleToRadian(angleDiff.vertical))*((m_resolutionWidth) / 2 )); screenPoint.Y = (float )(m_resolutionHeight / 2 + verticalDiff); return TRUE; } void Paint::changeBrush (COLORREF brushColor) { hBrush = CreateSolidBrush(brushColor); if (hdc&&hBrush) { DeleteObject(SelectObject(hdc, hBrush)); } } void Paint::changePen (int penStyle, int penWidth, COLORREF penColor) { hPen = CreatePen(penStyle, penWidth, penColor); if (hdc&&hPen) { DeleteObject(SelectObject(hdc, hPen) ); } } bool Paint::世界坐标转屏幕坐标_非矩阵(Point2D& 屏幕坐标, FLOAT 水平角度差, FLOAT 高低角度差){ getWndInfo(); FLOAT 高低可视角度 = (FLOAT)((double )atan2 (m_resolutionHeight, m_resolutionWidth) * 180 / 3.1415 ); if (fabs (水平角度差) > 45 || fabs (高低角度差) > 高低可视角度) { return false ; } int 水平差 = (int )(tan (水平角度差 * 3.1416 / 180 ) * ((m_resolutionWidth) / 2 )); 屏幕坐标.X = (float )(m_resolutionHeight / 2 + 水平差); int 高度差 = (int )(tan (高低角度差 * 3.1416 / 180 ) * ((m_resolutionWidth) / 2 )); 屏幕坐标.Y = (float )(m_resolutionHeight / 2 + 高度差); return true ; } bool Paint::worldPointToScreenPoint (Point2D & screenPoint,const Point3D& targetWorldPoint) { getWndInfo(); memcpy (&m_viewMatrix, (PVOID)m_matrixAddr32, sizeof (m_viewMatrix)); Vector4 worldLocation = { targetWorldPoint.X,targetWorldPoint.Y,targetWorldPoint.Z,1 }; Vector4 cutLocation= RowVecTimesMatrix(worldLocation, m_viewMatrix); if (cutLocation.w<0.0 ) { return false ; } Point2D NDC; NDC.X = cutLocation.x / cutLocation.w; NDC.Y = cutLocation.y / cutLocation.w; screenPoint.X = (NDC.X*m_resolutionWidth + m_resolutionWidth) / 2 ; screenPoint.Y = (m_resolutionHeight - m_resolutionHeight*NDC.Y) / 2 ; return true ; } bool Paint::worldPointToScreenPoint64 (Point2D & screenPoint, const Point3D& targetWorldPoint) { getWndInfo(); memcpy (&m_viewMatrix, (DWORD64*)m_matrixAddr64, sizeof (m_viewMatrix)); Vector4 worldLocation = { targetWorldPoint.X,targetWorldPoint.Y,targetWorldPoint.Z,1 }; Vector4 cutLocation = RowVecTimesMatrix(worldLocation, m_viewMatrix); if (cutLocation.w < 0.0f ) { return false ; } Point2D NDC; NDC.X = cutLocation.x / cutLocation.w; NDC.Y = cutLocation.y / cutLocation.w; screenPoint.X = (NDC.X*m_resolutionWidth + m_resolutionWidth) / 2 ; screenPoint.Y = (m_resolutionHeight - m_resolutionHeight*NDC.Y) / 2 ; return true ; } void Paint::paintLine (int x,int y) { getWndInfo(); MoveToEx(hdc, m_resolutionWidth / 2 , m_resolutionHeight, 0 ); LineTo(hdc, x, y); } void Paint::paintLine (int x, int y, int xTo, int yTo) { MoveToEx(hdc,x, y, 0 ); LineTo(hdc, xTo, yTo); } void Paint::paintFrame (int x,int y,int w,int h,int thick) { paintRect(x, y, w, thick); paintRect(x, y+thick, thick, h-thick); paintRect(x+w-thick, y+thick, thick, h-thick); paintRect(x+thick, y+h-thick, w-2 *thick, thick); } void Paint::paintFrameByFootAndHead (Point2D footPoiny, Point2D headPoint, int thick) { float head = headPoint.Y - footPoiny.Y; float width = head / 2 ; float center = width / -2 ; float extra = head / -6 ; paintFrame((int )(footPoiny.X + center), (int )footPoiny.Y, (int )width, (int )(head - extra), thick); } void Paint::paintRect (int x, int y, int w, int h) { RECT rect = { x,y,x + w,y + h }; FillRect(hdc, &rect, hBrush); } void Paint::paintText (int x, int y, COLORREF color, const char * text) { SetTextAlign(hdc, TA_CENTER | TA_NOUPDATECP); SetBkColor(hdc, RGB(0 , 0 , 0 )); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, color); DeleteObject(SelectObject(hdc, hfont)); TextOutA(hdc, x, y, text, strlen (text)); DeleteObject(hfont); } Vector4 RowVecTimesMatrix (const Vector4& rowVec, float matrix[16 ]) { Vector4 retVec; retVec.x = rowVec.x*matrix[0 ] + rowVec.y*matrix[4 ] + rowVec.z*matrix[8 ] + rowVec.w*matrix[12 ]; retVec.y = rowVec.x*matrix[1 ] + rowVec.y*matrix[5 ] + rowVec.z*matrix[9 ] + rowVec.w*matrix[13 ]; retVec.z = rowVec.x*matrix[2 ] + rowVec.y*matrix[6 ] + rowVec.z*matrix[10 ] + rowVec.w*matrix[14 ]; retVec.w = rowVec.x*matrix[3 ] + rowVec.y*matrix[7 ] + rowVec.z*matrix[11 ] + rowVec.w*matrix[15 ]; return retVec; }
Unity逆向 Unity一切基于GameObjects对象,他们可以有各种属性(unity中叫做components组件),有像transform的属性,其包含位置,旋转和比例等这样的数据.
unity的脚本由C#编写,建立在方法中,写在不同的类中以继承MonoBehaviour方法.
其中一些方法如下:
void Start() 这个方法只被调用一次
void Update() 这份方法每一个tick被调用一次
void FixedUpdate() 这个方法每一帧被调用一次
void OnGUI() 这是绘制方法
Unity的文件结构
Unity中所有的脚本被编译为两个DLLs,他们被称为 Assembly-CSharp 和 Assembly-CSharp-firstpass,他们在 Gamename_Date->Managed 文件夹中,其他的dll在文件夹中是引擎代码,和其他系统代码
Unity找数据特殊技巧: C#语言编译出来的dll中的语言实际上是IL(.NET框架中的中间语言),该语言的文件可以跨平台运行于mono 虚拟机.dnspy 可以将IL语言写的文件基本完美反编译出C#源代码.而il2cpp 的职责是把IL语言的文件转换为原生的系统二进制文件.
如何区分unity游戏是采用il2cpp技术还是不采用il2cpp技术
exe文件同目录下有GameAssembly.dll的一般为采用了il2cpp技术(Metadata文件夹所在目录没有什么dll),但可以进行加密隐藏该文件,参考原神
exe所在目录进入xxxData文件夹中的子目录中有个Metadata文件夹所在目录有一大堆dll文件(此时exe文件同目录下没有GameAssembly.dll)即为未采用il2cpp
il2cpp机制将C#中所有类型信息保存到global-metadata.dat的文件,通过解析global-metadata文件,可以获得C#代码中的类型,方法,字段等等信息
unity游戏偏移层级比较多,因此最优先的方法就是hook,在找到人物的基本属性(一般找血量)后找到赋值的汇编段inline hook取对象地址即可,这是最方便的做法
下面是不用hook的前提下快速找到数组:
数组和人数一般满足一定的偏移关系(不绝对)
而且unity可以直接找到人物的数组,没有其他对象干扰(找其他物品外部的方式似乎是通过找GameObjectManager).因此完全可以从人数入手 .
1 2 3 4 0 x********+0 x??为人数地址0 x********]+0x10 +0x4 *i为数组
逃离塔科夫逆向 **[1]**先追人物坐标
经过一个逆向注意点:下图这样的lea一个堆栈地址的情况,要如下图这么处理
未能最后,但追到下面的偏移:
1 [[[[[[rcx+40 ]+20 ]+10 ]+10 ]+38 ]+18 ]
**[2]**追骨骼
中途会遇到F2会崩溃的情况,此时只能使用硬件访问数据断点来断
放开断点前先跟踪往上追的内存,放开断点的时候,如果发现内存中的值骤变了,说明他是个临时的值,这时候,可以用CE搜的方式往上找
绘制相关 除了注入后的dll进行gdi绘制外,还有如下绘制方法
openGl和D3D绘制 简单粗暴,但容易被检测
opengl glBegin 开始渲染函数 一个参数 0 - 9 0是画一个点,1画线等等…
glDisable 关闭渲染函数 一个参数
hook glbegin函数,在hook中利用glDisable函数关闭我们不想让他显示的渲染
检测方面主要是 “CRC的对抗”
自建窗口外部绘制 反外挂最艰难的一种绘制方法
优点:不闪烁,检测难度大了
创建一个透明窗口
同步分辨率
透明窗口跟随游戏移动
置顶透明窗口
自己的窗口上画框
框架代码参考 /Desktop/FPS/csDLL
窗口特征枚举检测,截图检测( 可以使用SetWindowDisplayAffinity防止自己的外部窗口被截图)
相关代码 配置:
属性-VC++目录-包含目录:选中D3D9库根目录DirectSDK下的include文件夹
属性-VC++目录-引用目录:选中D3D9库根目录DirectSDK下的Lib\x86或Lib\x64文件夹
属性-VC++目录-库目录:选中D3D9库根目录DirectSDK下的Lib\x86或Lib\x64文件夹
预编译.h 1 2 3 4 5 6 7 8 9 10 11 12 13 #pragma once #include <d3d9.h> #include <d3dx9.h> #pragma comment(lib, "d3d9.lib" ) #pragma comment(lib, "d3dx9.lib" ) #include <dwmapi.h> #pragma comment(lib, "dwmapi.lib" ) #include <iostream> #include <Windows.h> using namespace std ;
D3D绘制.h 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #pragma once #include "预编译.h" static MARGINS Margin;static LPDIRECT3D9 g_pD3D = NULL ;static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL ;static D3DPRESENT_PARAMETERS g_d3dpp = {};static ID3DXLine* pLine = 0 ;static ID3DXFont* Font; static HWND 辅助窗口句柄, GameHwnd;static RECT 窗口矩形;static int 窗口宽, 窗口高; static WNDCLASSEX wClass; typedef void (*Draw) () ;static Draw Render; LRESULT WinProc (HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) ; bool 初始化D3D(); void 创建透明窗口(HWND 游戏窗口句柄, Draw 绘制函数); void 窗口消息循环(); void 画线(D3DCOLOR Color, float X1, float Y1, float X2, float Y2, float Width); void 绘制文字(float X, float Y, const char * Str, D3DCOLOR Color); void 画框(float X, float Y, float W, float H, float Width, D3DCOLOR Color); void 绘制开始(); void 绘制结束();
D3D绘制.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 #include "D3D绘制.h" bool 初始化D3D(){ if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL ) return false ; ZeroMemory(&g_d3dpp, sizeof (g_d3dpp)); g_d3dpp.Windowed = TRUE; g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; g_d3dpp.EnableAutoDepthStencil = TRUE; g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16; g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 辅助窗口句柄, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0 ) return false ; if (pLine == NULL ) D3DXCreateLine(g_pd3dDevice, &pLine); D3DXCreateFontW(g_pd3dDevice, 16 , 0 , FW_DONTCARE, D3DX_DEFAULT, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Vernada" , &Font); return true ; } void 创建透明窗口(HWND 游戏窗口句柄, Draw 绘制函数){ if (绘制函数 == NULL || 游戏窗口句柄 == 0 ) return ; GameHwnd = 游戏窗口句柄; Render = 绘制函数; wClass.cbClsExtra = NULL ; wClass.cbSize = sizeof (WNDCLASSEX); wClass.cbWndExtra = NULL ; wClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0 , 0 , 0 )); wClass.hCursor = LoadCursor(0 , IDC_ARROW); wClass.hIcon = LoadIcon(0 , IDI_APPLICATION); wClass.hIconSm = LoadIcon(0 , IDI_APPLICATION); wClass.hInstance = GetModuleHandle(NULL ); wClass.lpfnWndProc = (WNDPROC)WinProc; wClass.lpszClassName = L" " ; wClass.lpszMenuName = L" " ; wClass.style = CS_VREDRAW | CS_HREDRAW; if (RegisterClassEx(&wClass) == 0 ) { MessageBox(NULL , L"创建窗口出错!" , L"提示!" , 0 ); exit (1 ); } GetWindowRect(GameHwnd, &窗口矩形); 窗口宽 = 窗口矩形.right - 窗口矩形.left; 窗口高 = 窗口矩形.bottom - 窗口矩形.top; 辅助窗口句柄 = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, L" " , L" " , WS_POPUP, 1 , 1 , 窗口宽, 窗口高, 0 , 0 , 0 , 0 ); SetLayeredWindowAttributes(辅助窗口句柄, 0 , RGB(0 , 0 , 0 ), LWA_COLORKEY); ShowWindow(辅助窗口句柄, SW_SHOW); 初始化D3D(); } void 窗口消息循环(){ while (1 ) { if (GameHwnd) { GetWindowRect(GameHwnd, &窗口矩形); 窗口宽 = 窗口矩形.right - 窗口矩形.left; 窗口高 = 窗口矩形.bottom - 窗口矩形.top; DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE); if (dwStyle & WS_BORDER) { 窗口矩形.top += 23 ; 窗口高 -= 23 ; } MoveWindow(辅助窗口句柄, 窗口矩形.left, 窗口矩形.top, 窗口宽, 窗口高, true ); } MSG Message; ZeroMemory(&Message, sizeof (Message)); if (PeekMessage(&Message, 0 , 0 , 0 , PM_REMOVE)) { DispatchMessage(&Message); TranslateMessage(&Message); } Sleep(1 ); } if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL ; } if (g_pD3D) { g_pD3D->Release(); g_pD3D = NULL ; } CloseWindow(辅助窗口句柄); ::UnregisterClass(wClass.lpszClassName, wClass.hInstance); } LRESULT WinProc (HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_PAINT: if (g_pd3dDevice)Render(); break ; case WM_CREATE: DwmExtendFrameIntoClientArea(hWnd, &Margin); break ; case WM_DESTROY: { g_pD3D->Release(); g_pd3dDevice->Release(); exit (1 ); return 0 ; } default : return DefWindowProc(hWnd, Message, wParam, lParam); break ; } return 0 ; } void 画线(D3DCOLOR Color, float X1, float Y1, float X2, float Y2, float Width){ D3DXVECTOR2 Vertex[2 ] = { {X1,Y1},{X2,Y2} }; pLine->SetWidth(Width); pLine->Draw(Vertex, 2 , Color); } void 绘制文字(float X, float Y, const char * Str, D3DCOLOR Color){ RECT Rect = { (LONG)X,(LONG)Y }; Font->DrawTextA(NULL , Str, -1 , &Rect, DT_CALCRECT, Color); Font->DrawTextA(NULL , Str, -1 , &Rect, DT_LEFT, Color); } void 画框(float X, float Y, float W, float H, float Width, D3DCOLOR Color){ D3DXVECTOR2 Vertex[5 ] = { {X,Y},{X + W,Y},{X + W,Y + H},{X,Y + H},{X,Y} }; pLine->SetWidth(Width); pLine->Draw(Vertex, 5 , Color); } void 绘制开始(){ g_pd3dDevice->Clear(0 , 0 , D3DCLEAR_TARGET, 0 , 1.0f , 0 ); g_pd3dDevice->BeginScene(); } void 绘制结束(){ g_pd3dDevice->EndScene(); g_pd3dDevice->Present(0 , 0 , 0 , 0 ); }
调用例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include "D3D绘制.h" int 线粗 = 2 ;D3DCOLOR 红色 = D3DCOLOR_ARGB(255 , 255 , 255 , 255 ); void 绘制(){ 绘制开始(); 画线(D3DCOLOR_ARGB(255 , 0 , 0 , 255 ), 20 , 20 , 66 , 66 ,线粗); 画框(100 , 100 , 100 , 100 , 线粗, D3DCOLOR_ARGB(255 , 255 , 255 , 0 )); 绘制文字(200 , 200 , "吾无法无天" , D3DCOLOR_ARGB(255 , 255 , 0 , 255 )); 绘制结束(); } HWND 游戏窗口 = (HWND)0x50A00 ; void 开始(){ 创建透明窗口(游戏窗口, 绘制); 窗口消息循环(); } int main () { CreateThread(NULL , 0 , (LPTHREAD_START_ROUTINE)开始, NULL , 0 , NULL ); while (1 ) { cout << "输入233关闭:" << endl ; int a=0 ; cin >> a; if (a == 233 ) { return 0 ; } } return 0 ; }
GDI泄露 [注意点]: Create出来的GDI对象,都要用DeleteObject来释放;Create出来的DC,都要用DeleteDC来释放,GetDC得出的DC,要用ReleaseDC来释放。
getDC每次获取到的都不一样,我理解成开了个拷贝.因此即使是getDC(同一个窗口句柄) 多次,也要每一个getDC对应一个releaseDC才能防止gdi对象泄露
双缓冲 闪烁的原因是清除画布后,绘制的时间较长导致出现闪烁的现象
双缓冲使用内容缓冲来解决与多个画图操作相关的闪烁问题。 启用双缓冲后,所有画图操作会首先呈现到内存缓冲而不是屏幕上的绘图图面。 所有画图操作完成后,内存缓冲会直接复制到与之关联的绘图图面。 由于屏幕上仅执行一个图形操作,因此与复杂画图操作相关的图像闪烁可得以消除