Hughes'Blog


  • Home

  • Tags

  • Categories

  • Archives

CrackMe---001

Posted on 2019-10-26 | In learning

正式开始写CrackMe160题。
下面是CM001的WriteUp。

1

首先这个题目要求我们把Nag给干掉。
我想那好啊,直接根据API下断,把调用了MessageBoxA的地方直接NOP掉就完事了,结果一试才发现不对劲,这个调用的地方是系统领空,不是程序领空,不能直接NOP。
那接着能怎么办,老办法,搜索字符串。
搜索到后,回车来到代码部分,也没看见什么东西调用系统API,所以直接让程序执行到返回,然后把整个函数给NOP掉,这样级成功去除Nap了。

接着打开这个程序,发现它可以通过两种方式进行注册。一种是直接用序列码,另一种则是用ID和由ID生成的序列码注册。

2

先从简单的入手,直接输入序列码后提交,弹出对话框

发现有字符串可查,嘿嘿嘿,第一道题嘛,直接智能搜索字符串,果然搜到,然后找到在程序中的位置后

然后发现”Failed!”这些字符串是从jnz语句跳过去的,那肯定猜测这个跳转指令前面这个函数里有点东西,那么下断、步入这个CALL,进去后发现,其实也没什么东西。。。

简单得像张纸一样,直接明文对比,相等的话就通过je语句跳转到这个CALL返回的地方,那这个时候爆破也可以,直接查看注册码也可以。

可以看到,真正的序列码就是”Hello Dude!”.

3

接下来搞需要序列号和ID的。
同样的方法,利用程序提示错误的字符串进行搜索,发现有两处,我们分别下断,发现两处都有机会断下来.

2-1

我猜这个程序应该是有两个判断的机制,可能一个是直接依据ID格式来,另一个才是真正地去匹配。
先看在前面的,

惊喜地发现前面只是对ID的长度与4进行比较,如果大于等于的话就不会直接挂,那说明ID长度肯定是大于4的。

2-2

接着继续看后面的,

貌似这个思路和之前第一部分的思路是一样的,不相等就跳转,那肯定得看看这个跳转语句里面的内容了。同样的下断、步入,一进去就感觉离谱,里面怎么长这样:

这不我刚刚才写的注释嘛,下断试一下,发现貌似这段被调用了N多次,先不管这么多,先把序列号搞出来再说。
继续跟下去,然后查看寄存器里面的值,发现此时的密码就在EAX里面,但是这样子就张得有点离谱

猜测里面的数字应该是根据我输入的ID所生成的,又试了一次后发现果然是这样。现在要把程序生成密码的算法找出来。抓住密码存在EAX里面,我们可以往回找找。

可以看到,这个函数是个fast_call,调用的两个参数就存在[local.4],[local.3]这两个变量中,其中前者是我们输入的序列号,后者是程序根据我们输入的ID生成的序列号。
再往前看点,我们发现突然出现了密码的前缀和后缀,分别是CW和CRACKED。

那分别紧跟这这两个字符串的函数能有什么用,那肯定是拼接字符串了啊.既然能拼接字符串了,那么密码中间的部分肯定已经生成了吧,我们再往后看,发现出现了两个”-“,这不是密码中的间隔符号,嘛,那我们更加可以肯定密码是在这里完成拼接的了.那继续往前看,突然想起来我们之前不是发现了一个直接拿ID长度与4进行比较的跳转嘛,我们猜测密码是在跳转之后才生成的,毕竟你采取这样的方式跳转不就是为了提前判断ID是否有效嘛,如果ID都无效了,那还生成密码干什么嘛.
那我们先看看这两段之间的代码.

通过信息窗口,我们发现local.4就是ID的信息

然后查询内存,发现实际上就是取ID的第一个字节然后×上(00431750)这个地址里面双字长度的数


那么,通过查看之后,我们发现中间的数字就是通过取输入ID的前一个字×(0x29),然后再×2,最后转化为十进制就OK.
.
这个加密的算法就解出来了.经过测试后确实没错.

linux下常用指令

Posted on 2020-05-10 | In learning

linux常用命令

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
1. 文件目录
1.1 特殊符号:
1.1.1 . 当前目录
1.1.2 .. 上级目录
1.1.3 ~ 用户的home目录
1.2 cd - 切换目录
1.3 ls - 查看目录/文件属性
1.4 mv/cp/rm 剪切/拷贝/删除文件
1.5 rmdir 删除空目录
1.6 touch 创建新文件
1.7 查看文件内容 more(按页查看)/less(部分查看)/cat(查看全部)/head(查看前x行)/tail(查看后x行)
1.8 hexdump 以十六进制查看文件
hexdump -C 文件名
hexdump -C 文件名 | less
1.9 ln 创建文件链接
1.10 file - 查看文件类型
1.11 grep - 查找文件内容
1.12 find/whereis/whois
<!-- more -->
2. 用户/权限
2.1 useradd(命令)/adduser(脚本) 添加目录
2.1 passwd 修改用户登陆密码
2.3 userdel 删除用户
2.4 usermod 修改用户信息(用户名/主目录/用户组/登陆密码)
2.4 chmod 修改文件权限
2.5 chown 修改文件拥有者
2.6 groupadd/groupdel 添加用户组/删除用户组

3. 进程/任务
3.1 ps/top 查看当前进程信息
3.2 kill 结束进程

4. 磁盘
4.1 df/du 查看磁盘使用信息

5. 其它
clear - 清屏
source - 强行让一个脚本去立即影响当前的环境(不重启而更新配置)
apt - 软件包管理
更新源 sudo apt update
更新软件 sudo apt upgrade
安装软件 : sudo apt install 软件名
卸载软件 : sudo apt remove / sudo apt autoremove


6. 常见配置文件

GDB常用

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
1 命令行参数系列
1.1 set args
1.2 show args

2 运行系列
2.1 r - 运行
2.2 n - 单步步过(源码)
2.3 s - 单步步入
2.4 u - 运行到循环退出
2.5 c - 继续运行
2.6 si/ni - 汇编级单步步入/步过
2.7 jump - 设置eip到指定位置
2.8 finish 执行到当前函数返回
2.9 回车键 重复执行上一指令

3 断点系列
3.1 b 行号/函数名 - 设置断点
3.2 info b - 查看断点
3.3 d 断点号 - 删除断点
3.4 dis 断点号 - 禁用断点
3.5 enable 断点号 - 启用断点

4 信息查看系列
4.1 print 变量名 - 查看变量的值
4.2 set 变量名=VAL - 修改变量的值
4.3 set disassembly-flavor intel 以intel方式查看反汇编
4.4 list [函数名] - 查看源码
4.5 backtrace 查看函数的调用的栈帧和层级关系,简写bt
4.6 frame:切换函数的栈帧,简写f
4.7 x命令
格式 x/<n/f/u> <addr>
n是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果 地址是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
n/f/u三个参数可以一起使用。例如:

命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。
输出格式:
一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:

x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。

ELF文件结构

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
PE的组成

1. 头部
1.1 DOS头
1.2 NT头
1.2.1 文件头
1.2.2 扩展头
1.3 区段头
2. 区段数据
2.1 代码段: .text
2.2 数据段: .data
2.3 只读数据段: .rdata
2.3.1 导入表的数据
2.3.2 导出表的数据
2.4 重定位数据段: .reloc

ELF文件格式
ELF的组成部分

1. ELF头
1.1 id数组
1.1.1 前4位是魔数 0x74"elf"
1.1.2 ELF文件种类(32位/64位)
1.1.3 ELF文件数据的字节序(小端/大端)
1.1.4 版本 : 1. 文件版本,2. 系统ABI版本
1.2 文件类型 : 可重定位文件/可执行文件/动态库文件
2. 程序头
2.1 段类型 : 决定了段的作用,以及段是否被加载到内存中. 一个ELF文件中,至少存在一个LOAD类型的段. ELF一般都有解释器段,这个段用于保存解释器的文件路径. 有可能也会存在动态链接段(用于链接外部模块)
2.2 段数据的文件偏移/文件大小/内存地址/内存中的大小
2.3 加载到内存中后的分页属性
2.4 段的对齐粒度
3. 区段头
3.1 区段名
3.2 区段类型
3.3 Link和info
3.4 区段数据的文件偏移和大小,元素大小.
4. 区段数据
4.1 符号表
4.1.1 记录本模块的符号(导出)
记录在.symtab符号表中
记录一个符号的名字(例如函数名),记录了符号
的绑定类型(LOCAL,GLOBAL),还记录了符号的类型(函数,区段),记录符号的值(根据符号的不同,有不同的值,例如函数是符号类型,值就是函数的地址)
4.1.2 记录了导入的符号
记录在.dynsym符号表
4.2 重定位表
4.2.1 重定位类型, 决定了修复重定位的方法
可执行文件类型的ELF和共享库的ELF文件的重定位是不一样的.
4.2.2 重定位的偏移(根据重定位类型决定)

elf的加载
do_execve();

1. 其它
5.1 PLT和GOT表
call printf ==> jmp [GOT表项地址] =第二次以后=> printf真正的地址
​ push XX
​ jmp xxx ==> 加载printf所在的so文件,修复printf函数地址
​

2 构造函数数组,析构函数数组
2.1 全局对象的初始化和析构.
3 ptrace - 调试相关

(转载)数据逆向分析(3)

Posted on 2019-10-19 | In learning

结构体

struct是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。它的实现方法上和数组是一样的,即每一个成员的访问是直接寻址方式。唯一的区别是,为了提高访问效率,成员无论类型为何,编译器将它们按照4字节对齐。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
eg:struct test

{

char str;

int i;

};

test a={'A',0};

debug:

0041139E mov byte ptr [ebp-0Ch],41h //4字节对齐
004113A2 mov dword ptr [ebp-8],0

同样的尴尬

和数组一样,直接从二进制代码上判断结构体边界,并且它比数组的判断更难。尤其是自定义结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. struct TimeCount

{

time_t Begin;

time_t End;

};

TimeCount clock;//很好的书写习惯



2. tme_t Begin,End;//稍微懒一点

3. time_t a[2];//太恶劣了

这三个声明是等价的,struct的界定应该说很多时候是非常随意的,但是以下情况是必须准确判断:

1. 变量是结构体数组时,如果不想还原的代码,写满屏幕的话,嘿嘿...



2. 以指针形式在函数间传递时,好在这种情况很少是自定义结构体,这使得我们通常可以借助OD、IDA帮你判断。出现自定义结构体时,绝大多数情况又是队列、二叉树等等,只要经验足够的话是不难的。

3.4字节对齐产生数据空隙时,这么容易判断的时候,有便宜不占是白痴。

附上大佬的Blog链接

https://blog.csdn.net/yangbostar/article/list/2?

(转载)数据逆向分析(2)

Posted on 2019-10-19 | In learning

字符串

在C语言中,字符串是由字符组成,单字符不是字符串,至少有两个字符组成,且结尾字符是’/0’。

例如:”ABCD” 内存中实际储存结构是 41H 42H 43H 44H 00H

字符串的寻址,依靠首字节进行寄存器相对寻址方式:

例如:访问”ABCD”中的第二字节,byte ptr [ebx+1]

正是因为字符串的首字节索引的寻址方式,因此字符串往往要与指针配合。在C语言中常见的,

例如:char *str=“ABCD”;//mov [xxxx],offset string “ABCD”

另一种常见的方式,是将字符串放在数组里,需要首字节地址时,在数组名前加”&”伪装成指针。当然,存在数组里的字符串,自然可以按照数组的访问方式,比如 直接寻址。

数组

数组其实和普通变量没什么区别,每一个成员都是分别通过直接寻址方式访问。

但只有一种情况例外,并且十分常见,即当索引号为变量时,使用相对寻址方式。

例如:

1
2
3
char a[4]={'A','A','A','/0'};
for(int i=0;i<3;i++)
a[i]+=1;

对应反汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
char a[4]={'A','A','A','/0'};
0041139E mov byte ptr [a],41h
004113A2 mov byte ptr [ebp-7],41h
004113A6 mov byte ptr [ebp-6],41h
004113AA mov byte ptr [ebp-5],0

for(int i=0;i<3;i++)
004113AE mov dword ptr [i],0
004113B5 jmp wmain+40h (4113C0h)
004113B7 mov eax,dword ptr [i]
004113BA add eax,1
004113BD mov dword ptr [i],eax
004113C0 cmp dword ptr [i],3
004113C4 jge wmain+5Ah (4113DAh)

a[i]+=1;
004113C6 mov eax,dword ptr [i]
004113C9 movsx ecx,byte ptr a[eax]
004113CE add ecx,1
004113D1 mov edx,dword ptr [i]
004113D4 mov byte ptr [a+edx],cl ;反汇编器上常写为mov byte ptr a[edx],cl
004113D8 jmp wmain+37h (4113B7h)

数组的尴尬

除了源码调试,由于数组不标记边界,提通过数据初始化不能判断数组的始末位置
比如:

1
2
3
4
5
6
7
8
9
int i=0;
int a[3]={1,2,3};

OD反汇编结果:

00412FFE MOV DWORD PTR SS:[EBP-8], 0
00413005 MOV DWORD PTR SS:[EBP-1C], 1
0041300C MOV DWORD PTR SS:[EBP-18], 2
00413013 MOV DWORD PTR SS:[EBP-14], 3

显然,只看初始化我们很容易错误还原为 int a[4]={0,1,2,3};

因此数组的边界判断,需要后续代码流程的辅助,因为数组通常都是同一类操作的集合。

(转载)数据逆向分析(1)

Posted on 2019-10-19 | In learning

做数据逆向分析最先应该区分数据是常量、变量,还是指针。可是作为二进制数据本身不会标记自己是什么,然而编译器将不同的的类别储存在不同的区域。

变量数据应分为这两类:

1. 局部非静态变量:

对于1KB以下的该种变量,一般都要放在栈区,那么它的寻址必然用到ebp寄存器来间接变址进行寻址。另外,有些编译器(比如gcc)出于优化考虑,也会用到esp寄存器。

举例:
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
void fun()
{
int i=0;
char a[5]="AAAA";
}

//vc2005Debug版反汇编

void fun()
{

-------------------------------------------------保护寄存器数据
00411390 push ebp
00411391 mov ebp,esp
00411393 sub esp,0E0h
00411399 push ebx
0041139A push esi
0041139B push edi

---------------------为局部变量在栈区预留内存,以及vc编译器的添加防溢出措施

0041139C lea edi,[ebp-0E0h]
004113A2 mov ecx,38h
004113A7 mov eax,0CCCCCCCCh
004113AC rep stos dword ptr es:[edi]
004113AE mov eax,dword ptr [___security_cookie (417000h)] //研究溢出漏洞的人,

//可以看一下蓝色代码,

//通过在回调地址前,

//添加安全码

//使shellcode不可以覆盖

//这里,否则异常
004113B3 xor eax,ebp
004113B5 mov dword ptr [ebp-4],eax

//这是vs2005的新特性,

//你想能出破解方案吗?

-------------------------------------------------

int i=0

004113B8 mov dword ptr [ebp-0Ch],0
char a[5]="AAAA";
004113BF mov eax,dword ptr [string "AAAA" (41563Ch)]
004113C4 mov dword ptr [ebp-1Ch],eax
004113C7 mov cl,byte ptr ds:[415640h]
004113CD mov byte ptr [ebp-18h],cl
}

004113D0 push edx
004113D1 mov ecx,ebp
004113D3 push eax
004113D4 lea edx,[ (4113F4h)]
004113DA call @ILT+140(@_RTC_CheckStackVars@8) (411091h)
004113DF pop eax
004113E0 pop edx
004113E1 pop edi
004113E2 pop esi
004113E3 pop ebx
004113E4 mov ecx,dword ptr [ebp-4]
004113E7 xor ecx,ebp
004113E9 call @ILT+25(@__security_check_cookie@4) (41101Eh)
004113EE mov esp,ebp
004113F0 pop ebp
004113F1 ret

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
34
35
36

char a[5]="AAAA";
void fun()
{
static int i;
i=0;
a[0]='b';

}


void fun()
{
00411390 push ebp
00411391 mov ebp,esp
00411393 sub esp,0C0h
00411399 push ebx
0041139A push esi
0041139B push edi
0041139C lea edi,[ebp-0C0h]
004113A2 mov ecx,30h
004113A7 mov eax,0CCCCCCCCh
004113AC rep stos dword ptr es:[edi] //由于没有使用堆栈,编译器没有添加防溢出措施
static int i;
i=0;
004113AE mov dword ptr [41719Ch],0
a[0]='b';
004113B8 mov byte ptr [417034h],62h
}

004113BF pop edi
004113C0 pop esi
004113C1 pop ebx
004113C2 mov esp,ebp
004113C4 pop ebp
004113C5 ret

常量

常量和全局变量、静态变量实现方法使一样的,使用直接寻址,但区别是:只能读、不可写。
比如:

1
2
3
1. mov eax, [417034h]

2. cmp eax, [417034h]

指针

指针可以当做是存储地址的变量,它即可以是全局变量,也可以是局部非静态变量,但与普通变量的区别是:”初始化时使用offset或lea,使用指向内容时二次寻址”。

举例:初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.mov [417034h],offset “AAAA”//全局指针

2.lea eax,ARRAY

mov [esp-4ch],eax//局部指针

使用:

1.mov eax, [417034h]

mov edx, [eax]

2.mov eax, [esp-4ch]

mov edx, [eax+i]//指针可能指向的是数组

//vc2005Debug版反汇编

CE Tutorial Games--3

Posted on 2019-10-19 | In learning

CE Tutorial Games

跳转指令

Posted on 2019-10-18 | In learning

为了防止遗忘,以及为了日后查找方便,记录一下跳转指令的条件以及其意义

跳转指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ja 大于时跳转
jae 大于等于
jb 小于
jbe 小于等于
je 相等
jna 不大于
jnae 不大于或者等于
jnb 不小于
jnbe 不小于或等于
jne 不等于
jg 大于(有符号)
jge 大于等于(有符号)
jl 小于(有符号)
jle 小于等于(有符号)
jng 不大于(有符号)
jnge 不大于等于(有符号)
jnl 不小于
jnle 不小于等于
jns 无符号
jnz 非零
js 如果带符号
jz 如果为零

发生跳转时各标志寄存器的值

以及对应的指令字母的含义

1
2
3
4
5
6
7
8
a: above
e: equal
b: below
n: not
g: greater
l: lower
s: signed
z: zero

CE Tutorial Games--2

Posted on 2019-10-16 | In learning

##
参照文档:https://www.52pojie.cn/thread-915447-1-1.html
开始CE Tutorial Game 第三关的再次尝试。

1

2D游戏就两个坐标,按照52某牛的说法,2D游戏一般是左负右正,上下的正负不一定。

先利用CE搜索到人物的坐标。首先选到单浮点数和未知初始值,多次移动人物再加上多次搜索后就可以确定一个值了,这个就是我们要找的横坐标。
1
这时候修改这个横坐标的值就可以让人物进行瞬移了.
配合上上篇Blog里修改变绿台阶数为12即可过关.
现在我们来找一下任务的纵坐标.
一般横纵坐标的值应该都放在同一个地方,所以我们可以试下查看横坐标在内存中的相邻区域.
2
然后选择以浮点方式查看内存,接着让人物跳跃、移动,就可以发现两个可疑数据
3
观察数据变化可以发现,横坐标在屏幕内的范围从左到右是(-1,1),纵坐标在屏幕内的变化范围从上到下是(-1,1)

2

接下来试下找到游戏里面的重力系统.

2-1

重力可以改变纵坐标的值,所以我们查看下是什么改变纵坐标的值.
4
发现有两个指令对其进行修改,其中有一条指令在人物跳起来时不会写入数据.
猜测应该是第一条对纵坐标进行了修改.
进入内存查看,剔除掉和纵坐标无关的指令,剩下的代码由52大牛整理如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gtutorial-x86_64.exe+4048D - 48 8B 43 28           - mov rax,[rbx+28]
gtutorial-x86_64.exe+40491 - F3 44 0F10 40 28 - movss xmm8,[rax+28] { 读取 Y 坐标 }
gtutorial-x86_64.exe+40497 - 48 8B 43 28 - mov rax,[rbx+28]
gtutorial-x86_64.exe+4049B - F3 0F5A 48 28 - cvtss2sd xmm1,[rax+28] { 再次读取 Y 坐标 }
gtutorial-x86_64.exe+404A0 - F3 0F5A 53 78 - cvtss2sd xmm2,[rbx+78] { 读取 Y 速度 }
gtutorial-x86_64.exe+404A5 - F2 0F2A C6 - cvtsi2sd xmm0,esi { esi 是毫秒数 }
gtutorial-x86_64.exe+404A9 - F2 0F5E 05 AF382400 - divsd xmm0,[gtutorial-x86_64.exe+283D60] { 除以 1000 }
gtutorial-x86_64.exe+404B1 - F2 0F59 C2 - mulsd xmm0,xmm2 { 速度乘时间 }
gtutorial-x86_64.exe+404B5 - F2 0F5C C8 - subsd xmm1,xmm0 { Y 坐标减去位移 }
gtutorial-x86_64.exe+404B9 - F2 44 0F5A C9 - cvtsd2ss xmm9,xmm1 { double 转 float }

......

gtutorial-x86_64.exe+40506 - F3 44 0F11 48 28 - movss [rax+28],xmm9 { 赋值给 [rax+28] }

感觉整个分析过程就是要甄别出真正有效的数据,然后找出他们的内在逻辑.

纵坐标的地址可以很容易找出: [[["gtutorial-x86_64.exe"+37DC50]+760]+28]+28(他这里好像写错了)
根据分析,速度地址应该是 [["gtutorial-x86_64.exe"+37DC50]+760]+78
手动添加地址后我们就可以直接修改速度了,但是这个速度会变化,所以我们可以设置热键,让速度分别变为1和-1,这样就可以比较好地进行控制.
根据同样的道理,水平方向的速度我们也可以进行更改.

2-2

这次来找重力加速度.
重力加速度会直接影响竖直方向上的速度,我们直接查看什么修改了竖直方向上的速度.
5
发现只有第二条指令会一直对速度修改,所以在内存中查看第二条指令

1
2
3
4
gtutorial-x86_64.exe+40709 - F3 0F5A 43 78         - cvtss2sd xmm0,[rbx+78] { 读取速度 }
gtutorial-x86_64.exe+4070E - F2 0F5C 05 52362400 - subsd xmm0,[gtutorial-x86_64.exe+283D68] { 减去 0.1 }
gtutorial-x86_64.exe+40716 - F2 0F5A C0 - cvtsd2ss xmm0,xmm0 { double 转 float }
gtutorial-x86_64.exe+4071A - F3 0F11 43 78 - movss [rbx+78],xmm0 { 写入速度 }

这个机制蛮简单的,每次速度减0.1.

3

这次来找敌人的位置.
只要找到敌人的横坐标,它们的地址+4就是他们的纵坐标了.和搜索自己的坐标方法类似,很快可以搜索到他们的坐标.
直接锁定他们坐标的值,发现可以锁定,但是当12个台阶都亮起时,他们还是会飞去守门.
这时候查看什么对它们进行了写入,借用大牛的图:
6
直接将第五条指令nop掉,就可以对他们的坐标进行修改了.

4

这次来查找开门的指令.
开门和已经变绿的台阶数量有关系,那么我们先查看有什么访问了这个变量.
查看后发现只有一条指令堆积访问,于是查看它在内存中的位置,发现有几个有意思的判断:(还是摘自大牛整理的代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gtutorial-x86_64.exe+4098B - 48 63 93 88000000     - movsxd  rdx,dword ptr [rbx+00000088] { 读取已变绿平台数 }
gtutorial-x86_64.exe+40992 - 48 8B 43 30 - mov rax,[rbx+30]
gtutorial-x86_64.exe+40996 - 48 85 C0 - test rax,rax
gtutorial-x86_64.exe+40999 - 74 08 - je gtutorial-x86_64.exe+409A3
gtutorial-x86_64.exe+4099B - 48 8B 40 F8 - mov rax,[rax-08] { [rax-08] 为平台数组最大下标 }
gtutorial-x86_64.exe+4099F - 48 83 C0 01 - add rax,01 { 最大下标 + 1 即为总平台数 }
gtutorial-x86_64.exe+409A3 - 48 39 C2 - cmp rdx,rax { 比较已变绿平台数和总平台数 }
gtutorial-x86_64.exe+409A6 - 7C 17 - jl gtutorial-x86_64.exe+409BF
gtutorial-x86_64.exe+409A8 - 48 8B 43 60 - mov rax,[rbx+60] { 二级指针 }
gtutorial-x86_64.exe+409AC - C6 40 18 00 - mov byte ptr [rax+18],00 { 开门 }
gtutorial-x86_64.exe+409B0 - C6 43 7D 01 - mov byte ptr [rbx+7D],01 { 堵门 }
gtutorial-x86_64.exe+409B4 - 48 8B 43 68 - mov rax,[rbx+68]
gtutorial-x86_64.exe+409B8 - 48 89 83 80000000 - mov [rbx+00000080],rax
gtutorial-x86_64.exe+409BF - 48 83 7B 28 00 - cmp qword ptr [rbx+28],00 { 0 }

这里可以看到总台阶数并没有存在哪个变量里,而是由数组下标+1来表示.
我们可以推导出开门地址 [[["gtutorial-x86_64.exe"+37DC50]+760]+60]+18
以及[["gtutorial-x86_64.exe"+37DC50]+760]+7D
这样直接修改开门地址为0,堵门地址也为0就可以过关了.

内容太多了,下一篇Blog再来写破解碰撞检测的方法.

初学CheatEngine的使用(内含CE Tutorial Games--1)

Posted on 2019-10-14 | In learning

CheatEngine

很早就接触过了CE修改器,当时是玩一些幼稚小游戏,然后用ce修改器修改金币和经验。不过也仅仅是接触,从来没有深入过,这次CNSS招新有道贪食蛇的题目(wtcl,还没写出来),可以用CE调试,我就准备学下CE调试,然后刚刚好它有几个教程,这篇blog就来写下写CE教程题目的经历.
总共分为两部分,一个是Cheat Engine Tutorial,另一个是Cheat Engine Tutorial Games.

Cheat Engine Tutorial

这部分非常基础,然后基本上就是CE的基本用法.
第一关到第八关有手就行,然后第九关有一个点我没接触过,这里记录下来.
第九关是个小游戏,敌我双方各两个人,但是敌人伤害高血还厚,我们打不过,而过关的条件就是把敌人乃一组特.(如图)
第九关
首先肯定是搜索血量,很容易搜到四个人的血量的地址.然后F6,查看是什么改写这个地址.查看之后发现改写的时候调用的是同一个函数(如图).

Read more »

三种call的简要区别

Posted on 2019-10-14 | In learning

__stdcall,__cdecl,__fastcall的区别

Call_name | __stdcall | __cdecl | __fastcall
:-: | :-: | :-: | :-: | :-:
参数传递方式 | 右->左 压栈 | 右->左 压栈 | 左边开始的两个不大于4字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送
清理栈方式 | 被调用函数清理(即函数自己清理),多数据情况使用这个 | 调用者清理 | 被调用者清理
使用场合| WinAPI| C/C++ MFC默认方式 可变参数的时候使用|速度快
C编译修饰约定(它们均不改变输出函数名中的字符大小写)| 约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number|约定仅在输出函数名前加上一个下划线前缀,格式为_functionname|调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number

<12345>

44 posts
2 categories
31 tags
GitHub E-Mail
Links
  • Supergate
  • Weihongtao
  • Hujiekang
  • Centrix
  • CjDuan
  • Cool
© 2022