终于,今天我们来到了读取的循环
本系列已完结,以下是各章节说明,17 之前是 dos 版相关,之后是 2001 版:
while (local_6 != END_MARK_101) {
FUN_1048_0e02();
iVar3 = FUN_1048_0e3f();
if (iVar3 != local_12) {
(*DAT_1050_25e6)(0x1048,iVar3);
}
if (local_6 == RESET_MARK_100) {
init_dict();
local_4 = load_bits_by_len();
convert_to_pix(local_4);
}
else {
if (LAST_DICT_INDEX < local_6) {
uVar1 = FUN_1018_3be7(local_4);
}
else {
uVar1 = FUN_1018_3be7(local_6);
}
FUN_1018_3cdc(local_4,uVar1);
convert_to_pix(local_6);
local_4 = local_6;
cVar2 = FUN_1018_3bc3();
if (cVar2 != '\0') {
CURRENT_BIT_LEN = CURRENT_BIT_LEN + '\x01';
CURRENT_DICT_LEN = CURRENT_DICT_LEN << 1;
}
}
local_6 = load_bits_by_len();
local_12 = iVar3;
}
使用最长的 if else
大致可以分为三块:if else
前,if else
,if else
后。
if else
前包含 两个函数,一个函数指针
FUN_1048_0e02
int __cdecl16far FUN_1048_0e02(void)
{
int in_AX;
int in_CX;
if (1 < CPU_TYPE) {
return in_AX * in_CX;
}
return in_AX * in_CX;
}
汇编很复杂,但是 ghidra 认为只是计算了两个寄存器的积,我们看看 cpu log,看看里面放了什么:
0207:00003E3D call 0277:0E02 EAX:00000002 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005748 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0277:00000E02 cmp byte [1470],02 ds:[00001470]=0003 EAX:00000002 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
0277:00000E07 jc 00000E24 ($+1b) (no jmp) EAX:00000002 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0277:00000E09 shl eax,10 EAX:00000002 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0277:00000E0D shrd eax,edx,10 EAX:00020000 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:1 PF:1 IF:1
0277:00000E12 shl ecx,10 EAX:00000002 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0277:00000E16 shrd ecx,ebx,10 EAX:00000002 EBX:00010000 ECX:00640000 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:1 PF:1 IF:1
0277:00000E1B imul ecx EAX:00000002 EBX:00010000 ECX:00000064 EDX:00640000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0277:00000E1E shld edx,eax,10 EAX:000000C8 EBX:00010000 ECX:00000064 EDX:00000000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0277:00000E23 retf EAX:000000C8 EBX:00010000 ECX:00000064 EDX:00000000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005744 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:1 SF:0 OF:0 AF:0 PF:1 IF:1
似乎也就是 AX
乘了一个 64h
,那似乎也真是没什么用,我们看下一个函数。
FUN_1048_0e3f
int __cdecl16far FUN_1048_0e3f(void)
{
...
if (1 < CPU_TYPE) {
if (CONCAT22(in_BX,in_CX) != 0) {
return (int)(CONCAT22(in_DX,in_AX) / CONCAT22(in_BX,in_CX));
}
LAB_1048_0eb8:
iVar3 = FUN_1048_026d();
return iVar3;
}
...
}
虽然很长,但是根据寄存器和CPU 类型,其实也没什么看的。如果寄存器 BC 不为 0,就返回一个 DA/BC 的值,由于 BC 一直传入的是一个内存地址,基本不会为 0,所以这个看上去是一个偏移量的计算。每当偏移量发生变化时,它会去执行这个操作 (*DAT_1050_25e6)(0x1048,iVar3)
,但是这个地方 ghidra 看不到,不过我们还是有 cpu log:
0207:00003E5B call far word [25E6] ds:[000025E6]=3F04 EAX:0000002A EBX:00010000 ECX:00000008 EDX:00080000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005746 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0207:00003F04 enter 0038,00 EAX:0000002A EBX:00010000 ECX:00000008 EDX:00080000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005742 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0207:00003F08 leave EAX:0000002A EBX:00010000 ECX:00000008 EDX:00080000 ESI:00000070 EDI:00002608 EBP:00005740 ESP:00005708 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
0207:00003F09 retf 0002 EAX:0000002A EBX:00010000 ECX:00000008 EDX:00080000 ESI:00000070 EDI:00002608 EBP:00005758 ESP:00005742 DS:0287 ES:0287 FS:0000 GS:0000 SS:028F CF:0 ZF:0 SF:0 OF:0 AF:0 PF:0 IF:1
感觉什么都没有做,我们也先忽略好了。
这么一来 if else
之前,感觉也没做什么值得我们注意的事情。
if else
这应该就是重中之重了。
if
if (local_6 == RESET_MARK_100) {
init_dict();
local_4 = load_bits_by_len();
convert_to_pix(local_4);
}
如果读到了重设标记,则初始化字典,重读一个索引,没什么好说的。
else
else {
if (LAST_DICT_INDEX < local_6) {
uVar1 = FUN_1018_3be7(local_4);
}
else {
uVar1 = FUN_1018_3be7(local_6);
}
FUN_1018_3cdc(local_4,uVar1);
convert_to_pix(local_6);
local_4 = local_6;
cVar2 = FUN_1018_3bc3();
if (cVar2 != '\0') {
CURRENT_BIT_LEN = CURRENT_BIT_LEN + '\x01';
CURRENT_DICT_LEN = CURRENT_DICT_LEN << 1;
}
}
我们有几个函数要去看,首先是 FUN_1018_3be7
FUN_1018_3be7
undefined2 __stdcall16far FUN_1018_3be7(int param_1)
{
int iVar1;
undefined2 uVar2;
while (uVar2 = (undefined2)((ulong)DICT_START >> 0x10), iVar1 = (int)DICT_START,
*(int *)(iVar1 + param_1 * 4) != 0x7fff) {
param_1 = *(int *)(iVar1 + param_1 * 4);
}
return CONCAT11((char)((uint)(param_1 * 4) >> 8),*(undefined *)(iVar1 + param_1 * 4 + 2));
}
这似乎是,根据传入参数,去取一个索引,如果取出的索引是另一个索引,那么继续按新的索引去取,直到取出一个颜色,退出循环。
FUN_1018_3cdc
void __stdcall16far FUN_1018_3cdc(undefined2 param_1,undefined2 param_2)
{
undefined2 *puVar1;
undefined2 uVar2;
LAST_DICT_INDEX = LAST_DICT_INDEX + 1;
uVar2 = (undefined2)((ulong)DICT_START >> 0x10);
puVar1 = (undefined2 *)((int)DICT_START + LAST_DICT_INDEX * 4);
*puVar1 = param_1;
puVar1[1] = param_2;
return;
}
这个显然就是更新索引了。把上次的索引,和这次取到的颜色,写入字典。
FUN_1018_3bc3
undefined2 __cdecl16far FUN_1018_3bc3(void)
{
undefined local_3;
if ((CURRENT_DICT_LEN + -1 == LAST_DICT_INDEX) && (CURRENT_BIT_LEN != '\f')) {
local_3 = 1;
}
else {
local_3 = 0;
}
return CONCAT11((char)((uint)(CURRENT_DICT_LEN + -1) >> 8),local_3);
}
当字典索引没有到达字典长度减一时,返回 0。而我们根据 后续的判断,不为 0 时,字典的长度会进行扩容。
if else
后其实这段没什么了,读取一个索引,保存一下偏移量。 整个循环在没有读到结束标记时会一直继续下去。
感觉我们可以开始重写程序了。整理一下主要内容:
init_dict();
load_bits_by_len();
last_index = load_bits_by_len();
convert_to_pix(last_index);
current_index = load_bits_by_len();
while (current_index != END_MARK_101) {
if (current_index == RESET_MARK_100) {
init_dict();
last_index = load_bits_by_len();
convert_to_pix(last_index);
}
else {
if (LAST_DICT_INDEX < current_index) {
uVar1 = get_color_by_index(last_index);
}
else {
uVar1 = get_color_by_index(current_index);
}
add_dict_index(last_index,uVar1);
convert_to_pix(current_index);
last_index = current_index;
cVar2 = dict_len_check();
if (cVar2 != '\0') {
CURRENT_BIT_LEN = CURRENT_BIT_LEN + '\x01';
CURRENT_DICT_LEN = CURRENT_DICT_LEN << 1;
}
}
current_index = load_bits_by_len();
}
感觉不难对不对,其实我们也花了不少时间,下一篇,我们按这个思路去整理代码, 看会不会遇到什么阻力。