How to identify the device by pci capability id - bios

How to identify the device by pci capability id?
this is my code:
I try to access 34h and check if the capability id exists on the first loop
If it exists, it points to the next pointer, But there seems to be some problems in the steps of getting the pointer and putting the address.
'''
push eax
push edi
push esi
mov cx,100
;mov edi,[esi]
add edi,52 ;access 34h
lopreg:
mov eax,edi ;read
mov dx,0cf8h
out dx,eax
mov dx,0cfch
in eax,dx
cmp cx,100 ;first time
je first
cmp ah,10
jne nextreg
jmp ispcie
first:
cmp ah,0
je ending
sub edi,52
movzx bx,ah
add di,bx
loop lopreg
jmp ending
ispcie:
call set_cur
mov ah,09h
lea dx,regmem ;print pcie
int 21h
jmp ending
nextreg:
cmp al,0
je ending
movzx bx,al ;
add di,bx
loop lopreg
ending:
pop esi
pop edi
pop eax
ret
'''

This answer is written with the assumption that this code is looking for the PCI Express Capability.
There are several problems in this code.
At the first label, it should use al instead of ah to determine the offset of the first capability.
The Capability ID of the PCI Express capability is 10h, not 10.
After reading each capability header, al contains the capability id and ah contains the next pointer. So cmp ah, 10 should be cmp al, 10h .
At the nextreg label, it should use ah instead of al to determine the offset of the next capability.
On each iteration, it adds bx to di without removing the previous offset.
It's not clear what edi is initialized to, but if it does not have bit 31 set, then this code won't be reading PCI config space at all.
This should work:
mov cx,100
;mov edi,[esi]
add edi,52 ;access 34h
lopreg:
mov eax,edi ;read
mov dx,0cf8h
out dx,eax
mov dx,0cfch
in eax,dx
cmp cx,100 ;first time
je first
cmp al,10h
jne nextreg
jmp ispcie
first:
cmp al,0
je ending
sub edi,52
movzx bx,al
add di,bx
loop lopreg
jmp ending
ispcie:
call set_cur
mov ah,09h
lea dx,regmem ;print pcie
int 21h
jmp ending
nextreg:
cmp ah,0
je ending
sub di, bx
movzx bx,ah
add di,bx
loop lopreg
ending:
A better approach is to keep the original address in edi without changing it, and use lea eax, [edi+ebx] for each new offset.
There's no need to use ecx as a loop counter and the logic is a bit convoluted. It can be straightened out a bit to flow more clearly.
Here's how I would write it:
lea eax,[edi+34h]
mov dx,0cf8h
out dx,eax
mov dx,0cfch
in al,dx ; read offset of first capability
cmp al,0
je ending
movzx ebx,al
lopreg:
lea eax,[edi+ebx] ; offset of next capability is in ebx
mov dx,0cf8h
out dx,eax
mov dx,0cfch
in eax,dx ; read capability header
cmp al,10h ; check if it is the PCI Express capability
je ispcie
nextreg:
cmp ah,0 ; check if it is the end of the capability list
je ending
movzx ebx, ah ; set up ebx with the offset of the next capability
jmp lopreg
ispcie:
; base of device PCI config space is in edi
; offset of PCI Express Capability is in ebx
...
ending:
(I don't know what set_cur and regmem are so I didn't attempt to write that part of the code.)

Related

FASM - program doesn't work when using stack

I'd like to know why my code isn't working when I'm trying to use stack.
Program have to print name of itself.
Here is code without using stack, which is working:
format elf executable
entry _start
segment readable executable
strlen:
mov ebx,0
strlen_loop:
cmp byte [eax+ebx],0
je strlen_end
inc ebx
jmp strlen_loop
strlen_end:
inc ebx
ret
_start:
mov eax,[esp+4]
call strlen
mov eax,4
mov ecx,[esp+4]
mov edx,ebx
mov ebx,0
int 80h
mov eax,1
mov ebx,0
int 80h
Here is code with using stack (program just exits/doing nothing):
format elf executable
entry _start
segment readable executable
strlen:
mov ebx,0
strlen_loop:
cmp byte [eax+ebx],0
je strlen_end
inc ebx
jmp strlen_loop
strlen_end:
inc ebx
ret
_start:
mov eax,[esp+4]
call strlen
push ebx
mov eax,4
mov ebx,0
mov ecx,[esp+4]
pop edx
int 80h
mov eax,1
mov ebx,0
int 80h
I'm new in Linux development, but if I understood your problem, you have a mistake in this block:
mov eax,4
mov ebx,0
mov ecx,[esp+4] ; you wrote 'push' instruction above so value of 4 is incorrect now
pop edx
int 80h
You can just swap these two lines to fix this:
pop edx
mov ecx,[esp+4]

How to change x86 opcode from compiled code

Below is block of the code of an app I am reversing.
Please forgive me I new to world of reversing but I figured out that something is happening here and I thought I must change je to jmp but I am not sure if it's correct.
_unit34::TApplication.Run
Push ebp
ebp,esp
ecx
Push ebx
Push esi
Push edi
Mov dword ptr [ebp-4],eax
Mov. Eax,dword ptr [ebp-4]
Mov byte ptr [eax+0AD],1
XOR edx,edx
Push ebp
Push. 4E2B09
Push dword ptr fs:[edx]
Mov dword ptr fs:[edx],esp
Mov Eax,4D6574; DoneApplication
Call AddExitProc
Mov eax, dword ptr [ebp-4]
Mov eax, dword ptr [ebp+44]
Test eax,eax
Je 004E2AF1
Can someone explain a bit of what's happening there?
And how can I change je to jmp using Interactive Delphi reconstructor?
Can someone explain a bit of what's happening there?
Push ebp
mov ebp,esp
Sets up a stack frame.
??? ecx
Push ebx
Push esi
Push edi
Save non-volatile registers
Mov dword ptr [ebp-4],eax
Save the Self pointer
Mov. Eax,dword ptr [ebp-4]
Unfortunate stack trashing :-(
Mov byte ptr [eax+0AD],1
Self.SomeBoolean:= true
XOR edx,edx
Push ebp
Push. 4E2B09
Push dword ptr fs:[edx]
Mov dword ptr fs:[edx],esp
try
Mov Eax,4D6574; DoneApplication
Call AddExitProc
Mov eax, dword ptr [ebp-4]
Set the exit proc for the application
Mov eax, dword ptr [ebp+44]
SomeObject:= somevar
Test eax,eax
Je 004E2AF1
If (Assigned(SomeObject)) then .....
And how can I change je to jmp using Interactive Delphi reconstructor?
That particular change should make no sense here, because it would invoke an exception. The je is invoked when the object is nil. This is the error condition. You will change the program to always crash with an error.
However if you absolutly must do it, just change the opcode from $74 to $EB (for a byte offset jump) or from $0F84 to $90E9 (for a long jump).
I'd use a hex editor.
None of these changes will have a happy outcome unless you know exactly what you're doing.
Where to start
Get expertise in Delphi coding and view a lot of CPU output.
Press Ctrl + Alt + C to see what code Delphi produces.
This will take a number of months to years to master however.
About reversing
If you want to get into reversing IDA pro is a much better tool.
It has a freeware edition you can start with.

Delphi assembler: understanding the Result register

I'm messing around with ASM in Delphi. From my understanding, EAX holds Result. In the following, I have to put RET at the end, otherwise Result is not correct (it is correct if the input is 0). What am I doing wrong, or should I say, what don't I understand about this?
function MSb(const Val: Integer): Integer;
label
Go;
asm
CMP EAX, 0
JNZ Go
MOV EAX, -1
RET
Go:
BSR EBX, EAX
MOV EAX, EBX
RET
end;
If I say the following:
MOV Result, EBX
Then I get the following compilation:
MOV [EPB-$04], EBX
MOV EAX, [EPB-$04]
However, my code above has the following postscript:
MOV EAX, EDX
RET
At least without optimization enabled, your function has a pre-amble and a post-amble. Have a look at it:
Project46.dpr.13: asm
0041A1F4 55 push ebp
0041A1F5 8BEC mov ebp,esp
0041A1F7 83C4F8 add esp,-$08
0041A1FA 8945F8 mov [ebp-$08],eax
Project46.dpr.14: CMP EAX, 0
0041A1FD 83F800 cmp eax,$00
Project46.dpr.15: JNZ Go
0041A200 7506 jnz $0041a208
Project46.dpr.16: MOV EAX, -1
0041A202 B8FFFFFFFF mov eax,$ffffffff
Project46.dpr.17: RET
0041A207 C3 ret
Project46.dpr.19: BSR EBX, EAX
0041A208 0FBDD8 bsr ebx,eax
Project46.dpr.20: MOV EAX, EBX
0041A20B 89D8 mov eax,ebx
Project46.dpr.21: RET
0041A20D C3 ret
Project46.dpr.22: end;
0041A20E 8B45FC mov eax,[ebp-$04]
0041A211 59 pop ecx
0041A212 59 pop ecx
0041A213 5D pop ebp
0041A214 C3 ret
So the pre-amble sets up the stack frame. It saves away the ebp register, and modifies both the ebp and esp registers. Notice also the post-amble. You do need to execute that code to restore the saved registers.
The usual way to deal with this is to jump to the end of the function instead of using ret. So write your code like this:
function MSb(const Val: Integer): Integer;
asm
CMP EAX, 0
JNZ ##go
MOV EAX, -1
JMP ##exit
##go:
BSR EBX, EAX
MOV EAX, EBX
##exit:
end;
This way you ensure that the post-amble is executed. You really should get into the habit of writing the code this way to ensure that any pre-amble is executed.
Now, beyond that I suspect that the problem you mention in the question actually relates to a compiler bug relating to your use of a Pascal label rather than an asm label. Well, perhaps it is a compiler bug, but perhaps it is just a mistake to use a Pascal label. Consider the following program:
{$APPTYPE CONSOLE}
function MSb(const Val: Integer): Integer;
asm
CMP EAX, 0
JNZ ##Go
MOV EAX, -1
JMP ##exit
##Go:
BSR EBX, EAX
MOV EAX, EBX
##exit:
end;
function MSb2(const Val: Integer): Integer;
label
Go;
asm
CMP EAX, 0
JNZ Go
MOV EAX, -1
RET
Go:
BSR EBX, EAX
MOV EAX, EBX
end;
begin
Writeln(Msb(0));
Writeln(Msb(1));
Writeln(Msb2(0));
Writeln(Msb2(1));
Readln;
end.
The output when compiled with optimization is:
-1
0
-1
4
So, what about that rather odd 4. Well, let's look at the assembled code for Msb2, which is essentially your code:
004059E8 83F800 cmp eax,$00
004059EB 7506 jnz $004059f3
004059ED B8FFFFFFFF mov eax,$ffffffff
004059F2 C3 ret
004059F3 0FBDD8 bsr ebx,eax
004059F6 89D8 mov eax,ebx
004059F8 8BC2 mov eax,edx
004059FA C3 ret
Why on earth is the value of edx, a volatile register whose value has not been assigned, being moved into eax just before the function returns. This is the problem that you are reporting. My guess is that the use of a Pascal label is confusing the assembler. Stick to asm labels.
Here is the assembled code for Msb:
004059D4 83F800 cmp eax,$00
004059D7 7506 jnz $004059df
004059D9 B8FFFFFFFF mov eax,$ffffffff
004059DE C3 ret
004059DF 0FBDD8 bsr ebx,eax
004059E2 89D8 mov eax,ebx
004059E4 C3 ret
That's more like it! Notice how the compiler knows that there is no post-able here, and replaces the jmp ##exit with a straight ret.
What am I doing wrong, or should I say, what don't I understand about
this?
Basically as clearly stated the pre and post-ambles but also be careful when using assembler code. Parameters are passed in different ways depending upon calling conventions. Your code will run fine with the Pascal (ironically obsolete) calling convention, if you remove the label, the RET and use assembler labels (##). It is good practice to always indicate a calling convention for assembly code, since Result may refer indeed to EAX or a local variable for holding it. EAX maps directly to Result when using the Register (default) calling convention.

Experimental OS in assembly - can't show a character on the screen (pmode)

I hope there's some experienced assembly/os developer here, even if my problem is not a huge one.
I am trying to play with assembly and create a small operating system. In fact, what I want is a boot-loader and a second boot-loader that activates pmode and displays a single char on the screen, using the video memory (not with interrupts, evidently).
I am using VirtualBox to emulate the code, which I paste manually inside a VHD disk (two sectors of code)
In first place, my code:
boot.asm
This is the first boot-loader
bits 16
org 0
mov al, dl
jmp 07c0h:Start
Start:
cli
push ax
mov ax, cs
mov ds, ax
mov es, ax
pop ax
sti
jmp ReadDisk
ReadDisk:
call ResetDisk
mov bx, 0x1000
mov es, bx
mov bx, 0x0000
mov dl, al
mov ah, 0x02
mov al, 0x01
mov ch, 0x00
mov cl, 0x02
mov dh, 0x00
int 0x13
jc ReadDisk
jmp 0x1000:0x0000
ResetDisk:
mov ah, 0x00
mov dl, al
int 0x13
jc ResetDisk
ret
times 510 - ($ - $$) db 0
dw 0xAA55
boot2.asm
This is the second boot-loader, pasted on the second sector (next 512 bytes)
bits 16
org 0
jmp 0x1000:Start
InstallGDT:
cli
pusha
lgdt [GDT]
sti
popa
ret
StartGDT:
dd 0
dd 0
dw 0ffffh
dw 0
db 0
db 10011010b
db 11001111b
db 0
dw 0ffffh
dw 0
db 0
db 10010010b
db 11001111b
db 0
StopGDT:
GDT:
dw StopGDT - StartGDT - 1
dd StartGDT + 10000h
OpenA20:
cli
pusha
call WaitInput
mov al, 0xad
out 0x64, al
call WaitInput
mov al, 0xd0
out 0x64, al
call WaitInput
in al, 0x60
push eax
call WaitInput
mov al, 0xd1
out 0x64, al
call WaitInput
pop eax
or al, 2
out 0x60, al
call WaitInput
mov al, 0xae
out 0x64, al
call WaitInput
popa
sti
ret
WaitInput:
in al, 0x64
test al, 2
jnz WaitInput
ret
WaitOutput:
in al, 0x64
test al, 1
jz WaitOutput
ret
Start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ax, 0x9000
mov ss, ax
mov sp, 0xffff
sti
call InstallGDT
call OpenA20
ProtectedMode:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 08h:ShowChar
bits 32
ShowChar:
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h
pusha ; save registers
mov edi, 0xB8000
mov bl, '.'
mov dl, bl ; Get character
mov dh, 63 ; the character attribute
mov word [edi], dx ; write to video display
popa
cli
hlt
So, I compile this code and paste the binary in the VHD, then run the system on Virtual Box. I can see that it goes in pmode correctly, the A20 gate is enabled and the LGTR contains a memory address (which I have no idea if is the correct). This is some part of the log file, that may be of interest:
00:00:07.852082 ****************** Guest state at power off ******************
00:00:07.852088 Guest CPUM (VCPU 0) state:
00:00:07.852096 eax=00000011 ebx=00000000 ecx=00010002 edx=00000080 esi=0000f4a0 edi=0000fff0
00:00:07.852102 eip=0000016d esp=0000ffff ebp=00000000 iopl=0 nv up di pl zr na po nc
00:00:07.852108 cs={1000 base=0000000000010000 limit=0000ffff flags=0000009b} dr0=00000000 dr1=00000000
00:00:07.852118 ds={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr2=00000000 dr3=00000000
00:00:07.852124 es={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr4=00000000 dr5=00000000
00:00:07.852129 fs={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr6=ffff0ff0 dr7=00000400
00:00:07.852136 gs={0000 base=0000000000000000 limit=0000ffff flags=00000093} cr0=00000011 cr2=00000000
00:00:07.852141 ss={9000 base=0000000000090000 limit=0000ffff flags=00000093} cr3=00000000 cr4=00000000
00:00:07.852148 gdtr=0000000000539fc0:003d idtr=0000000000000000:ffff eflags=00000006
00:00:07.852155 ldtr={0000 base=00000000 limit=0000ffff flags=00000082}
00:00:07.852158 tr ={0000 base=00000000 limit=0000ffff flags=0000008b}
00:00:07.852162 SysEnter={cs=0000 eip=00000000 esp=00000000}
00:00:07.852166 FCW=037f FSW=0000 FTW=0000 FOP=0000 MXCSR=00001f80 MXCSR_MASK=0000ffff
00:00:07.852172 FPUIP=00000000 CS=0000 Rsrvd1=0000 FPUDP=00000000 DS=0000 Rsvrd2=0000
00:00:07.852177 ST(0)=FPR0={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852185 ST(1)=FPR1={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852193 ST(2)=FPR2={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852201 ST(3)=FPR3={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852209 ST(4)=FPR4={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852222 ST(5)=FPR5={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852229 ST(6)=FPR6={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852236 ST(7)=FPR7={0000'00000000'00000000} t0 +0.0000000000000000000000 ^ 0
00:00:07.852244 XMM0 =00000000'00000000'00000000'00000000 XMM1 =00000000'00000000'00000000'00000000
00:00:07.852253 XMM2 =00000000'00000000'00000000'00000000 XMM3 =00000000'00000000'00000000'00000000
00:00:07.852262 XMM4 =00000000'00000000'00000000'00000000 XMM5 =00000000'00000000'00000000'00000000
00:00:07.852270 XMM6 =00000000'00000000'00000000'00000000 XMM7 =00000000'00000000'00000000'00000000
00:00:07.852280 XMM8 =00000000'00000000'00000000'00000000 XMM9 =00000000'00000000'00000000'00000000
00:00:07.852287 XMM10=00000000'00000000'00000000'00000000 XMM11=00000000'00000000'00000000'00000000
00:00:07.852295 XMM12=00000000'00000000'00000000'00000000 XMM13=00000000'00000000'00000000'00000000
00:00:07.852302 XMM14=00000000'00000000'00000000'00000000 XMM15=00000000'00000000'00000000'00000000
00:00:07.852310 EFER =0000000000000000
00:00:07.852312 PAT =0007040600070406
00:00:07.852316 STAR =0000000000000000
00:00:07.852318 CSTAR =0000000000000000
00:00:07.852320 LSTAR =0000000000000000
00:00:07.852322 SFMASK =0000000000000000
00:00:07.852324 KERNELGSBASE =0000000000000000
00:00:07.852327 ***
00:00:07.852334 Guest paging mode: Protected (changed 5 times), A20 enabled (changed 2 times)
So, this is the status of the processor at the end of the test.
The problem is that, I cannot see the character on the screen. This can be a problem related to memory (I must admit I'm not so good at memory addressing), like wrong content in segment register, or it can be related to the manner in which I am trying to use the video memory in order to show that character, but it may be something else. What do you think is wrong? Thanks so much!
Update
The problem is related to memory addressing. The ShowChar instructions are not executed. I verified it in the logs file. What I know is that everything is executed correctly up to this line:
jmp 08h:ShowChar
So, this might be related to wrong segment registers, wrong GDTR or something else related to memory addressing.
Update
I changed GDT, to be a linear address instead of a segment:offset one, but still not seeing the character. The problem is that I can't figure out the origin of the problem, because I can't verify if the GDT is correct. I can see the content of all the registers, but how could I know that the GDTR (which at the moment is 0000000000ff53f0:00e9) is correct? I'm just supposing that the ShowChar function is not executed because of a wrong GDT, but just a supposition.
The problem is, despite all your work for making character and attribute available in DX:
mov bl, '.'
mov dl, bl ; Get character
mov dh, CHAR_ATTRIB ; the character attribute
you end up writing word 63 into the screen buffer:
mov word [edi], 63 ; write to video display
which is a question mark with zero attributes, i.e. black question mark on black background.
I'm not very experienced with this, but...
GDT:
dw StopGDT - StartGDT - 1
dd StartGDT
Doesn't this need to be an "absolute" (not seg:offs) address? Since you've loaded it at segment 1000h, I would expect dd StartGDT + 10000h to be right here. No?
Here is a workable minimalist bootloader that switch to protected and print a "X" to VGA, using Qemu (so no need to read the disk).
[org 0x7C00]
cli
lgdt [gdt_descriptor]
; Enter PM
mov eax, cr0
or eax, 0x1
mov cr0, eax
; 1 GDT entry is 8B, code segment is 2nd entry (after null entry), so
; jump to code segment at 0x08 and load init_pm from there
jmp 0x8:init_pm
[bits 32]
init_pm :
; Data segment is 3rd entry in GDT, so pass to ds the value 3*8B = 0x10
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
;Print a X of cyan color
;Note that this is printed over the previously printed Qemu screen
mov al, 'L'
mov ah, 3 ; cyan
mov edx, 0xb8004
mov [edx], ax
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]
GDT:
;null :
dd 0x0
dd 0x0
;code :
dw 0xffff ;Limit
dw 0x0 ;Base
db 0x0 ;Base
db 0b10011010 ;1st flag, Type flag
db 0b11001111 ;2nd flag, Limit
db 0x0 ;Base
;data :
dw 0xffff
dw 0x0
db 0x0
db 0b10010010
db 0b11001111
db 0x0
gdt_descriptor :
dw $ - GDT - 1 ;16-bit size
dd GDT ;32-bit start address
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Bootsector padding
times 510-($-$$) db 0
dw 0xaa55
Then, do:
nasm boot.asm
qemu boot
To write to video memory in standard VGA you write data to memory address 0xb8000 or byte 8000 in memory. To do a simple black and white character simply OR the character value with the value 15 << 8 so that you get a 16 bit unsigned short. You then write these 16 bits to that memory location to draw the character.
The problem is your use of the ORG directive and mixing up real mode and protected mode addressing schemes. You are right about your 32 bit code not being executed. When the CPU executes this code:
jmp 08h:ShowChar
It jumps to somewhere in the currently loaded Interrupt Vector Table, at the beginning of memory instead of your 32 bit code. Why? Because the base of your defined code segment is 0, and you told your assembler to resolve addresses relative to 0:
Org 0
Thus the CPU is actually jumping to an address that is numerically equal to (0 + the offset of the first instruction of your ShowChar code) (i.e. Code Segment Base + Offset)
To rectify this issue, change:
Org 0
Into
Org 0x10000
Then you would need to change your segment registers to match, but in this case the segment registers you originally set were incorrect for the origin directive you originally specified, but are valid when the origin directive is changed as above, so no further changes need to be made. As a side note, the fact that your origin directive was incorrect can explain why your GDT address appeared to be garbage - because it was in fact some part of the Interrupt Vector Table that was loaded by your lgdt instruction. Your pointer to the GDT parameters ('GTD' label) is actually pointing to somewhere in the beginning of the Interrupt Vector Table.
Anyway, simply changing the origin directive as shown above should fix the problem.
By the way, your code looks awfully similar to the code over at http://www.brokenthorn.com/Resources/OSDev8.html
Especially the demo code provided at the bottom of the page of
http://www.brokenthorn.com/Resources/OSDev10.html
Interesting..

Implementation details for SystemPropertiesAdvanced.exe (specifically SYSDM)

I'm investigating how C:\Windows\System32\SystemPropertiesAdvanced.exe on Windows 2008 interacts with explorer.exe when updating system environment variables. When updating environment variables this way, explorer.exe will dynamically update its environment block to pick up the changes. I'm aware of multiple ways, to remotely inject environment variables, but was curious as to the implementation SystemPropertiesAdvanced.exe uses.
When I press the OK button on the SystemPropertiesAdvanced dialog after updating an environment variable, a new thread is created. Debugging this thread I've come to the following lines of interest:
WINSTA!WinStationBSMWorkerThread:
75b292bc 8bff mov edi,edi
75b292be 55 push ebp
75b292bf 8bec mov ebp,esp
75b292c1 51 push ecx
75b292c2 8365fc00 and dword ptr [ebp-4],0
75b292c6 53 push ebx
75b292c7 56 push esi
75b292c8 8b7508 mov esi,dword ptr [ebp+8]
75b292cb 837e4000 cmp dword ptr [esi+40h],0
75b292cf 57 push edi
75b292d0 0f95c0 setne al
75b292d3 50 push eax
75b292d4 ff763c push dword ptr [esi+3Ch]
75b292d7 8d4624 lea eax,[esi+24h]
75b292da ff7638 push dword ptr [esi+38h]
75b292dd ff7628 push dword ptr [esi+28h]
75b292e0 50 push eax
75b292e1 ff7620 push dword ptr [esi+20h] ds:0023:00404468={SYSDM!szUserEnv (6b3ec434)}
75b292e4 8d4614 lea eax,[esi+14h]
75b292e7 ff761c push dword ptr [esi+1Ch]
75b292ea ff7618 push dword ptr [esi+18h]
75b292ed 50 push eax
75b292ee ff7610 push dword ptr [esi+10h]
75b292f1 ff760c push dword ptr [esi+0Ch]
75b292f4 ff7608 push dword ptr [esi+8]
75b292f7 ff7604 push dword ptr [esi+4]
75b292fa ff36 push dword ptr [esi]
75b292fc e821fdffff call WINSTA!WinStationBroadcastSystemMessageWorker (75b29022)
75b29301 8d7e44 lea edi,[esi+44h]
75b29304 57 push edi
75b29305 894508 mov dword ptr [ebp+8],eax
75b29308 ff15d810b275 call dword ptr [WINSTA!_imp__EnterCriticalSection (75b210d8)]
75b2930e 33db xor ebx,ebx
75b29310 43 inc ebx
75b29311 837e3000 cmp dword ptr [esi+30h],0
75b29315 0f85663d0000 jne WINSTA!WinStationBSMWorkerThread+0x5b (75b2d081)
75b2931b 57 push edi
75b2931c 895e2c mov dword ptr [esi+2Ch],ebx
75b2931f ff15d410b275 call dword ptr [WINSTA!_imp__LeaveCriticalSection (75b210d4)]
75b29325 837dfc00 cmp dword ptr [ebp-4],0
75b29329 0f85683d0000 jne WINSTA!WinStationBSMWorkerThread+0x7c (75b2d097)
75b2932f 8b4508 mov eax,dword ptr [ebp+8]
75b29332 5f pop edi
75b29333 5e pop esi
75b29334 5b pop ebx
75b29335 c9 leave
75b29336 c20400 ret 4
I believe that the call to WINSTA!WinStationBroadcastSystemMessageWorker is some how letting explorer.exe know that it should pick up these new environment variables. It's interesting that the reference at 75b292e1 is being identified as SYSDM!szUserEnv by the debugger.
Perhaps not your standard inquiry, but maybe there are other reverse engineers out there. :)
[at 100rep I'll set the "tags" properly]
Cheers
Pradeep Prem Kamal explains how to use WM_SETTINGCHANGE and SendMessageTimeout to update all top-level processes in his blog post here. Another example (in Delphi) can be found here.
My guess is that WINSTA!WinStationBroadcastSystemMessageWorker wraps updating the user environment variables (SYSDM!szUserEnv ?) and makes the SendMessageTimeout call.

Resources